主線程有個事件循環(huán)。每次有事件的時候,由cocoa框架調(diào)用我們寫的代碼,再回到cocoa框架。NSAutoreleasePool其實是由cocoa框架管理的,當(dāng)出了我們寫的代碼,回到框架的時候,放入NSAutoreleasePool的對象就會由框架調(diào)用release, 這樣就保證跑我們代碼的時候,對象是會存在的,出了我們的代碼,對象也能夠正常地被釋放。
比如
系統(tǒng)代碼(事件激發(fā))
funA()
funB()
funC()
funD()
系統(tǒng)代碼(等待事件)
在funA, funB, funC, funD中,無論調(diào)用多少層,也是用戶寫的代碼。autorelease的對象還是存在的。下一次事件激發(fā)的時候,上一次事件的對象是不存在的,因為已經(jīng)被系統(tǒng)框架調(diào)用release回收了。除非你自己調(diào)用了retain, 自己調(diào)用了一次retain, 就應(yīng)該自己來調(diào)用一次release, 這樣也沒有違背上面說的原則。
自動釋放池的缺點:它延緩了對象的釋放,在有大量自動釋放的對象時,會占用大量內(nèi)存資源。因此,你需要避免將大量對象自動釋放。并且,在以下兩種情況下,你需要手動建立并手動銷毀掉自動釋放池:
1.當(dāng)你在主線程外開啟其它線程時:系統(tǒng)只會在主線程中自動生成并銷毀掉自動釋放池。(runloop未開啟)
2.當(dāng)你在短時間內(nèi)制造了大量自動釋放對象時:及時地銷毀有助于有效利用iPad上有限地內(nèi)存資源
iOS的運行時是由一個一個runloop組成的,每個runloop都會執(zhí)行下圖的一些步驟:
可以看到,每個runloop中都創(chuàng)建一個Autorelease Pool,并在runloop的末尾進(jìn)行釋放,
所以,一般情況下,每個接受autorelease消息的對象,都會在下個runloop開始前被釋放。也就是說,在一段同步的代碼中執(zhí)行過程中,生成的對象接受autorelease消息后,一般是不會在代碼段執(zhí)行完成前釋放的。
當(dāng)然也有讓autorelease提前生效的辦法:自己創(chuàng)建Pool并進(jìn)行釋放
NSAutoreleasePool* pool = [[NSAutoreleasePoolalloc] init];
NSArray* array = [[[NSArrayalloc] init] autorelease];
[pool drain];
上面的array就會在[pool drain]執(zhí)行時被釋放。
所以對于你遇到的問題,可以在for循環(huán)外嵌套一個Autorelease Pool進(jìn)行管理,例如
NSAutoreleasePool* pool = [[NSAutoreleasePoolalloc] init];
for(inti =0; i <10000; i++)
{// ...}
[pool drain];
但由于你提到了生成的每個實例可能會比較大。只在循環(huán)外嵌套,可能導(dǎo)致在pool釋放前,內(nèi)存里已經(jīng)有10000個實例存在,造成瞬間占用內(nèi)存過大的情況。
因此,如果你的每個實例僅需要在單次循環(huán)過程中用到,那么可以考慮可以在循環(huán)內(nèi)創(chuàng)建pool并釋放
for(inti =0; i <10000; i++)
{NSAutoreleasePool* pool = [[NSAutoreleasePoolalloc] init];
// ...[pool drain];}