Cocoa記憶體管理方法:retain、release和autorelease
概要:
每個對象都維護一個保留計數器:對象建立時其保留計數器值為1,對象被保留時計數器+1;對象釋放時保留計數器-1;當保留計數器值為0時對象被銷毀,在銷毀對象時,首先調用對象的dealloc方法,再回收其佔用的記憶體以供其他對象使用
當對象接受一條autorelease訊息時,其保留計數器值不會立即改變,相反該對象只是被放入到NSAutoreleasePool中。當自動釋放池被銷毀時,會向池中所有對象發送release訊息,所有被自動釋放的對象都將其保留計數器值-1。如果保留計數器值為0則對象被銷毀。
當使用AppKit時,Objective-C會在適當的時間為你建立和銷毀自動釋放池。
記憶體管理黃金法則
1、使用new、alloc、copy方法建立一個對象時,該對象的計數器值為1.當不再使用該對象時需要向該對象發送一條release或autorelease訊息
2、當通過其他方法獲得一個對象時,假設該對象的計數器為1且已經被設定為自動釋放,則不需要執行任何操作來確保該對象被清理
3、如果保留了某個對象,最終需要釋放或自動釋放該對象,必須保持retain方法和release方法的使用次數相等
簡單的說:如果你使用了new、alloc或copy方法獲得一個對象,則你必須釋放或自動釋放該對象
一、臨時對象
未打算長期擁有該對象:如果你使用new、alloc、copy方法獲得一個對象,則需要安排該對象消亡。通常使用release訊息來實現
eg:
NSMutableArray *array;
array=[[NSMutableArray alloc] init];//count=1
[array release];//count=0
注意例外:
如果使用其他方法獲得一個對象例如arrayWithCapacity:方法則不需要關心如果銷毀該對象:
NSMutableArray *array;
array=[NSMutableArray arrayWithCapacity:10];//count=1 autoreleased
原因:
arrayWithCapacity方法不屬於alloc、new、copy這3個方法中的一個,因此可以假設該對象被返回時保留計算機值為1且已經被設定為自動釋放。
當自動釋放池被銷毀時,向array對象發送release訊息,該對象保留計算機值歸0,佔用的記憶體被回收
還有NSColor類也是
NSColor *color;
color=[NSColor blueColor];
blueColor方法也不屬於alloc、new、copy這3方法。解釋同上
二、擁有對象
希望一直擁有該對象,常見方法:在其他對象的執行個體變數中使用這些對象將它們加入到例如:NSArray或NSDictionary等集合中或作為全域變數使用。
如果你正在使用new、alloc或copy建立一個對象則該對象保留計數器值為1也一直被保留但一定要在擁有該對象的對象的dealloc方法中釋放該對象
-(void) doStuff
{
flonkArray =[NSMutableArray new];//count 1
}
-(void) dealloc
{
[flonkArray release];//count 0
[super dealloc];
}
上面說你是通過new、alloc、copy建立對象,如果不實用這三個擷取對象從其他方式獲得建立對象,並且需要擁有對象則考慮編寫GUI應用程式事件迴圈情況
eg:使用自動釋放對象,上面代碼可以修改為:
-(void) doSuff { flontArray =[NSMutableArray arrayWithCapacity:10];//count 1 autorelease(為什麼會自動銷毀原因在上面) [flontArray retain];//count 2 autorelease 這裡如果不實用retain那麼flontArray可能會被銷毀。原因就是arrayWithCapacity } -(void) dealloc { [flontArray release];//count 1 [super dealloc]; }
當事件迴圈結束或自動釋放池被銷毀時,flontArray將會接收到一個release訊息使得計數器值從2減到1,計數器值>0所以對象繼續存在。因此需要到dealloc中釋放該對象。
那在看看這個例子
int i; for(i=0;i<1000000;i++) { id object=[someArray objectAtIndex:i]; NSString *desc=[object description];//description 一些方法這裡不需要理他 }
如果這樣寫可能會產生100萬個description字串對象,記憶體佔用會持續的增長,這100萬個對象是一直存在的直到自動釋放池被銷毀時才最終釋放。
解決方案:
解決的方法就是在迴圈中建立一個自己的自動釋放池,這樣使得每執行1000次就銷毀當前自動釋放池並建立一個新的自動釋放池:
NSAutoreleasePool *pool; pool =[[NSAutoreleasePool alloc] init]; int i; for(i=0;i<1000000;i++){ id object=[someArray objectAtIndex:i]; NSString *desc=[object description]; if(i%1000==0){ [pool release]; pool =[[NSAutoreleasePool alloc] init]; } } [pool release]
每執行1000次新的自動釋放池就被銷毀,同時新的自動釋放池建立。另外自動釋放池中的description字串也不會超過1000條。從而使得記憶體佔用不會連續增加;最終使得自動釋放池分配和銷毀的操作代價變得很小;
補充說明:
自動釋放池是以棧的形式出現:當你建立一個新的自動釋放池時,它將會被添加到棧頂(棧的工作方式先進先出);接收autorelease訊息的對象將被放到最頂端的自動釋放池中。如果將一個對象放入到自動釋放池中再建立一個新的自動釋放池並銷毀該建立的自動釋放池,則這個自動釋放池對象仍將存在,因為容納該對象的自動釋放池仍然存在。
三、記憶體回收
Objective-C 記憶體回收行程是一種繼承性的記憶體回收行程和自動釋放池一樣記憶體回收行程也是在事件迴圈結束時觸發,但要注意:如果開發iPhone軟體則不能使用記憶體回收行程,實際上在編寫iPhone程式時,蘋果公司建議不要在自己的代碼中使用autorelease方法同時還要避免使用建立自動釋放對象的便利函數。
在XCode中啟用下記憶體回收功能(項目資訊視窗Build選項卡並選擇Required[-fobjc-gc-only]選項即可)即可具體沒有mac 也沒具體操作,那天買個真機試試再來補充……