文章轉自:http://www.2cto.com/kf/201404/296070.html
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;
}
我們發現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消息)。可以看出,一個自動釋放的對象,它至少能夠存活到自動釋放池銷燬的時候。
-(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.NSArray的arrayWithObjects:和arrayWithArray:。
2.UIImage的imageNamed:。
3.NSNumber的numberWithBool等。
也就是說這些方法返回的對象,我們可以用,但是還是不能確定得知道她什麼時候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資源。