部分內容摘自《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.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資源。