52個(gè)有效方法(34) - 以“自動(dòng)釋放池塊”降低內(nèi)存峰值

  • Objective-C對(duì)象的生命周期取決于其引用計(jì)數(shù)。

  • 在Objective-C的引用計(jì)數(shù)架構(gòu)中,有一項(xiàng)特性叫做“自動(dòng)釋放池”(autorelease pool)。釋放對(duì)象有兩種方式:一種是調(diào)用release方法,使其保留計(jì)數(shù)立即遞減;另一種是調(diào)用autorelease方法;將其加入“自動(dòng)釋放池”中。自動(dòng)釋放池用于存放那些需要在稍后某個(gè)時(shí)刻釋放的對(duì)象。清空(drain)自動(dòng)釋放池時(shí),系統(tǒng)會(huì)像其中的對(duì)象發(fā)送release消息。

  • 不管對(duì)象是在自動(dòng)釋放池內(nèi)還是外創(chuàng)建的,只要在自動(dòng)釋放池內(nèi)調(diào)用autorelease方法;這個(gè)對(duì)象就會(huì)被放到自動(dòng)釋放池中。注意autorelease方法只有在自動(dòng)釋放池中使用才有效。

  • @autoreleasepool{}可以隨意創(chuàng)建,也可以嵌套使用。一般情況下,無需擔(dān)心自動(dòng)釋放池的創(chuàng)建問題。iOS應(yīng)用程序運(yùn)行在Cocoa Touch環(huán)境中。系統(tǒng)會(huì)自動(dòng)創(chuàng)建一些線程,比如說主線程或GCD機(jī)制中的線程,這些線程默認(rèn)都有自動(dòng)釋放池,每次執(zhí)行“事件循環(huán)(event loop)”,就會(huì)將其清空。因此,不需要自己創(chuàng)建“自動(dòng)釋放池塊”。通常只有一個(gè)地方需要?jiǎng)?chuàng)建自動(dòng)釋放池,那就是在main函數(shù)里。

//寫在main函數(shù)里的這個(gè)自動(dòng)釋放池可以理解為最外圍捕捉全部自動(dòng)釋放對(duì)象所用的池。
int main(int argc, char * argv[]) {

    @autoreleasepool {
    
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
        
    }
    
}
  • 將自動(dòng)釋放池嵌套的好處是,可以借此控制應(yīng)用程序的內(nèi)存峰值,使其不致過高。
for (int i = 0; i < 1000; i++) {
    [self doSomethingWithInt:i];
}
  • 如果“doSomethingWithInt:”方法要?jiǎng)?chuàng)建臨時(shí)對(duì)象,那么這些對(duì)象很可能會(huì)放在自動(dòng)釋放池里。然而釋放池要等線程執(zhí)行下一次事件循環(huán)時(shí)才會(huì)清空。這就意味著在執(zhí)行for循環(huán)時(shí),會(huì)持續(xù)有新對(duì)象創(chuàng)建出來,并加入自動(dòng)釋放池中。所有這種對(duì)象都要等for循環(huán)執(zhí)行完才會(huì)釋放。這樣一來,在執(zhí)行for循環(huán)時(shí),應(yīng)用程序所占內(nèi)存量就會(huì)持續(xù)上漲,而等到所有臨時(shí)對(duì)象都釋放后,內(nèi)存量又會(huì)突然降下來。

  • 如果把循環(huán)內(nèi)的代碼包裹在“自動(dòng)釋放池塊”中,那么在循環(huán)中自動(dòng)釋放的對(duì)象就會(huì)放在這個(gè)池,而不是線程的主池里面。

NSArray * databaseRecords = /* . . . */;
NSMutableArray *people = [NSMutableArray new];
for (NSDictionary *record in databaseRecords) {
    @autoreleasepool {
        EOCPerson * person = [[EOCPerson alloc]  initWithRecord:record];
        [people addObject:person];
    }
}
  • 加上這個(gè)自動(dòng)釋放池之后,應(yīng)用程序在執(zhí)行循環(huán)時(shí)的內(nèi)存峰值就會(huì)降低,不再像原來那么高了。內(nèi)存峰值(high-memory waterline)是指應(yīng)用程序在某個(gè)特定時(shí)段內(nèi)的最大內(nèi)存用量(highest memory footprint)。

  • 自動(dòng)釋放池機(jī)制就像“棧”(stack)一樣。系統(tǒng)創(chuàng)建好自動(dòng)釋放池之后,就將其推入棧中,而清空自動(dòng)釋放池,則相當(dāng)于將其從棧中彈出。在對(duì)象上執(zhí)行自動(dòng)釋放操作,就等于將其放入棧頂?shù)哪莻€(gè)池里。

  • 盡管自動(dòng)釋放池塊的開銷不太大,但畢竟還是有的,所以盡量不要建立額外的自動(dòng)釋放池。

  • @autoreleasepool語法還有個(gè)好處:每個(gè)自動(dòng)釋放池都有其范圍,可以避免無意間誤用了那些在清空池后已為系統(tǒng)所回收的對(duì)象。

要點(diǎn)
  1. 自動(dòng)釋放池排布在棧中,對(duì)象收到autorelease消息后,系統(tǒng)將其放入最頂端的池里。

  2. 合理運(yùn)用自動(dòng)釋放池,可降低應(yīng)用程序的內(nèi)存峰值。

  3. @autoreleasepool這種新式寫法能創(chuàng)建出更為輕便的自動(dòng)釋放池。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容