iOS內存管理之:引用計數、ARC、自動釋放池autoreleasepool和便捷方法之間的關係

文章轉自:http://www.2cto.com/kf/201404/296070.html

iOS內存管理之:引用計數、ARC、自動釋放池autoreleasepool和便捷方法之間的關係

2014-04-27      0 個評論    來源:iOS內存管理之:引用計數、ARC、自動釋放池autoreleasepool和便捷方法之間的關係   
收藏    我要投稿

部分內容摘自《Objective-C基礎教程》和互聯網

引用計數

Cocoa採用了引用計數(reference counting)機制,每一個對象有一個關聯的“整數retainCount”用於記錄對象的使用情況。對象被引用時retaincount+1,外部環境結束對象的使用後retainCount-1.當retaincount爲0的時候,該對象被銷燬。

當我們使用alloc、new或者copy的我們需要銷燬這個對象。release函數,只是將對象的retainCount值減1,並不是刪除對象。當retainCount==0的時候,系統會發給對象一個dealloc消息,另外:千萬不要手動調用dealloc,因爲我們不知道何時,何地,何人還會使用該對象。應該老老實實依賴引用計數機制完成內存管理。

釋放對象所有權的函數除了release還有autorelease,這是一種延遲操作,下面會詳細介紹。


當我們看到下面代碼,

第一個問題:dateformatter的內存管理,應該很好理解,因爲[NSDateFormatter alloc]所以,我們要release它。

-(NSString*) date2String:(NSString*)str

{

NSString* dateString;

NSDate * currentTime=[NSDate date];

NSDateFormatter *dateformatter=[[NSDateFormatter alloc] init];

[dateformatter setDateFormat:str];

dateString =[dateformatter stringFromDate:currentTime];

[dateformatter release];

return dateString;

}

那麼,這個函數中的NSString* dateString 和NSDate * currentTime這兩個變量,在怎樣進行內存管理。還有,局部dateString作爲返回值,內存管理又是如何?這是個好問題。 

我們發現dateString的賦值方法是 [ dateformatter stringFromDate:current ] ,顯然,它並沒有使用alloc、new或copy任何一種。《Objective-C基礎教程》上說:假設dateString對象被返回時保留引用計數值爲1。呵呵,“假設”倆字。這本書還真是基礎教程!

剛纔《Objective-C基礎教程》說過,OC裏沒有棧上對象,沒有臨時對象。那麼這個dateString算是什麼?

那麼,現在將他們放到自動釋放的範疇,可以這麼理解:[ dateformatter stringFromDate:current ] 裏面alloc新的對象。這個對象就是autorelease的。

下面將詳細介紹自動釋放。

自動釋放池autoreleasepool

自動釋放池是NSAutoreleasePool的實例,其中包含了收到autorelease消息的對象。當一個自動釋放池自身被銷燬(dealloc)時,它會給池中每一個對象發送一個release消息(如果你給一個對象多次發送autorelease消息,那麼當自動釋放池銷燬時,這個對象也會收到同樣數目的release消息)。可以看出,一個自動釋放的對象,它至少能夠存活到自動釋放池銷燬的時候。

簡單的說一個例子,返回局部堆上變量的指針(用c++的口吻說的),那麼這個對象如何釋放?Objective-C發明了自動釋放機制。

-(obj*) foo

{

obj* temp = [[obj alloc]init];

return [ obj autorelease];//只是在返回的時候加上關鍵字autorelease

}

《Objective-C基礎教程》上說:自動釋放(autorelease)是一種延遲釋放機制,這樣保證局部堆上的變量能夠被外部正常使用。

但是,系統又是什麼時候釋放的呢?在每一個事件週期(event cycle)的開始,系統會自動創建一個自動釋放池;

在每一個事件週期的結尾,系統會自動銷燬這個自動釋放池。一般情況下,你可以理解爲:當你的代碼在持續運行時,自動釋放池是不會被銷燬的,這段時間內你也可以安全地使用自動釋放的對象;當你的代碼運行告一段落,開始等待用戶輸入(或者其它事件)時,自動釋放池就會被釋放掉,池中的對象都會收到一個release消息,有的可能會因此被銷燬。

這是很難確定的時間,如果自動釋放池的銷燬時間過早,那麼程序就很危險,這個恐怕很難滿足程序員的要求吧。

自動釋放池的缺點:它延緩了對象的釋放,在有大量自動釋放的對象時,會佔用大量內存資源。因此,你需要避免將大量對象自動釋放。並且,在以下兩種情況下,你需要手動建立並手動銷燬掉自動釋放池:

1.當你在主線程外開啓其它線程時:系統只會在主線程中自動生成並銷燬掉自動釋放池。

2.當你在短時間內製造了大量自動釋放對象時:及時地銷燬有助於有效利用iPad上有限地內存資源。


所以,本人不建議使用autorelease的機制,如果遇到上面例子的情況,使用典型的解決方法吧,外部一個對象負責刪除obj對象,防止內存泄露。

Convenience method的內存管理

與自動釋放相關的,有一大類構造方法(constructor method),由它們構造的對象直接就是自動釋放的對象;這一類構造方法叫做便捷方法。比如下面這句的字符串就是一個自動釋放的對象,stringWithFormat:就是一個便捷方法。

NSString* string = [NSString stringWithFormat:@”autoreleaseString”];

再舉幾個便捷方法的例子,方便讀者以後的開發。

1.NSArrayarrayWithObjects:和arrayWithArray:。

2.UIImageimageNamed:。

3.NSNumbernumberWithBool等。

也就是說這些方法返回的對象,我們可以用,但是還是不能確定得知道她什麼時候dealloc,那麼我們能不能主動刪除這些“便捷函數”返回的對象呢?如果,程序中有大量的“便捷函數”,這樣無疑佔用了大量內存空間。

難道只能避免循環調用這種“便捷函數”?

現在我們已經解釋了,autorelease方法會在一段時間以後釋放掉一個對象,在這段時間內我們可以安全地使用該對象。那麼這段時間究竟是多久呢?

上面已經介紹了自動釋放的機制,“便捷函數”產生的對象至少能夠存活到自動釋放池銷燬的時候。

ARC(自動引用計數Auto Reference counting)

上面的文字介紹了“引用計數”這裏又來個更高級的自動引用計數。

請參考這篇文章 http://blog.csdn.net/zkdemon/article/details/7446385


/****************************************下面說以下典型的應用****************************?

self.xxx的作用。

NSInteger i =0;

第一行 _extraMessage = [[FtExportMessage alloc]init];

第二行 //self.extraMessage = [[FtExportMessage alloc]init];

i = [self.extraMessage retainCount];

[self.extraMessage release];


你會發現:運行第一行時,retainCount是1,這個好理解。但是不要使用第二行代碼,retaincount是2,及時這個時候你調用release也不會刪除對象。

初學者容易犯錯,什麼地方都用self.XXX.


NSArray和NSDictionary的添加元素,內存管理

這種集合類,只是讓“元素”的retainCount加1.同樣,當NSDictionary release的時候,會將“元素”

- (void)prepareData

{

_buddyMsg = [[ExportMsgEntity alloc] init];

_pgMsg = [[ExportMsgEntity alloc] init];;

_dgMsg = [[ExportMsgEntity alloc] init];

_msgGroup = [[NSMutableDictionary alloc] initWithObjectsAndKeys:

self.buddyMsg,KMsgBuddy,

self.pgMsg,KMsgPGGroup,

self.dgMsg,KMsgDGGroup,nil];

int i=0;

i = self.buddyMsg.retainCount;//此時 i=2

}

上面的代碼使self.buddyMsg的retainCount從1加1成爲2.那麼,當NSDictionary析構後呢,請看下面的情況

- (void)deleteDictionary

{

[self.msgGroup release];

int i=0;

i = self.buddyMsg.retainCount;//此時 i=1

}

上面代碼使得self.buddyMsg的retainCount從2減1成爲1

總結:使用這些“集合”的時候,不要妄想着“集合”release的時候會自動刪除裏面的元素。最後還是元素自己release資源。

發佈了71 篇原創文章 · 獲贊 0 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章