例子1
一個老程序員,功成名就,金盆洗手不在寫代碼后,決定練練書法。提筆思索良久后在紙上寫下:Hello world!
for (int i = 0 ; i < largeNumber; i++) {
NSString *myStr = @"Hello world"; // 原諒我用Hello world
myStr = [myStr stringByAppendingString:[NSString stringWithFormat:@"-%05d-",i]];
}
那么會有什么問題呢?
如果largeNumber不大時沒有問題,但是當它很大時問題非常嚴重!造成內存泄露。雖然ARC會自動釋放內存,但是ARC內存的釋放,即全局的自動釋放池是當完成一次消息循環(huán)才會釋放。當我們使用for循環(huán)創(chuàng)建很多個使用autorelease方式創(chuàng)建的NSString對象的時候,將所有的對象的釋放權都交給了一個全局的釋放池(RunLoop 的釋放池),而RunLoop的釋放池會等待這個事件處理之后才會釋放,因此就會使對象無法及時釋放,堆積在內存造成內存泄露。
代碼應該這樣修改:添加一個局部的自動釋放池,那么每執(zhí)行一次循環(huán)就會釋放一次,則不會造成內存泄露
for (int i = 0 ; i < largeNumber; i++) {
@autoreleasepool {
NSString *myStr = @"Hello world";
myStr = [myStr stringByAppendingString:[NSString stringWithFormat:@"-%05d-",i]];
}
}
例子2
我們來看一個自動釋放的例子。一個所有者先用alloc方法創(chuàng)建一個對象;此時該所有者擁有這個對象,對象的引用計數(shù)為1。緊接著,所有者自動釋放該對象;所有者此時已經(jīng)放棄了所有權,但對象的引用計數(shù)在一段時間內依然為1。我們可以看出自動釋放的另一個好處:你不會因為在后面忘記給對象發(fā)送release消息而造成內存泄露。
- (Object *)returnAutoreleaseObject
{
Object *obj = [[Object alloc] init];
return [obj autorelease];
}
現(xiàn)在我們已經(jīng)解釋了,autorelease方法會在一段時間以后釋放掉一個對象,在這段時間內我們可以安全地使用該對象。那么這段時間究竟是多久呢?
我們需要先更多地了解自動釋放的機制,再來回答這個問題。讓我們先來看看自動釋放池。自動釋放池是NSAutoreleasePool的實例,其中包含了收到autorelease消息的對象。當一個自動釋放池自身被銷毀(dealloc)時,它會給池中每一個對象發(fā)送一個release消息(如果你給一個對象多次發(fā)送autorelease消息,那么當自動釋放池銷毀時,這個對象也會收到同樣數(shù)目的release消息)。可以看出,一個自動釋放的對象,它至少能夠存活到自動釋放池銷毀的時候。那么自動釋放池何時被創(chuàng)建,又何時被銷毀呢?在每一個事件周期(event cycle)的開始,系統(tǒng)會自動創(chuàng)建一個自動釋放池;在每一個事件周期的結尾,系統(tǒng)會自動銷毀這個自動釋放池。一般情況下,你可以理解為:當你的代碼在持續(xù)運行時,自動釋放池是不會被銷毀的,這段時間內你也可以安全地使用自動釋放的對象;當你的代碼運行告一段落,開始等待用戶輸入(或者其它事件)時,自動釋放池就會被釋放掉,池中的對象都會收到一個release消息,有的可能會因此被銷毀。到此為止,相信你已經(jīng)對自動釋放的機制有了一個大體的了解。自動釋放而非直接釋放,可以幫助你節(jié)省一些代碼量,提高開發(fā)速度。但是它有一個直接的缺點:它延緩了對象的釋放,在有大量自動釋放的對象時,會占用大量內存資源。因此,你需要避免將大量對象自動釋放。并且,在以下兩種情況下,你需要手動建立并手動銷毀掉自動釋放池: 1.當你在主線程外開啟其它線程時:系統(tǒng)只會在主線程中自動生成并銷毀掉自動釋放池。 2.當你在短時間內制造了大量自動釋放對象時:及時地銷毀有助于有效利用iPad上有限地內存資源。