局部釋放池和RunLoop釋放池的概念:
主線程的RunLoop是默認開啟的. 每一次消息循環(huán)開始的時候會先創(chuàng)建自動釋放池,這次循環(huán)結(jié)束前,會釋放自動釋放池之剧,然后RunLoop等待下次事件源。 在這個過程中砍聊,由RunLoop創(chuàng)建的釋放池類似于一個全局的釋放池背稼。但是開發(fā)者可以任何執(zhí)行的地方創(chuàng)建釋放池,也就是局部的釋放池玻蝌,這時的釋放池類似于代碼塊 當釋放池結(jié)束的時候會自動釋放蟹肘。因此一般情況下,局部的自動釋放池很快就被釋放了俯树,而RunLoop釋放池會等一次消息循環(huán)結(jié)束的時候釋放帘腹。
什么樣的對象會交給釋放池管理:
返回當前類的實例的類方法創(chuàng)建出來的對象,都是autorelease的,會交給所在的釋放池進行管理。 例如創(chuàng)建一個Person類,使用[[self alloc]init]方法創(chuàng)建的對象的管理不會交給它所在的釋放池盐类,而是根據(jù)引用計數(shù)來控制釋放的時機, 如果使用[[[self alloc]init] autorelease]創(chuàng)建的對象试伙,會交給所在的釋放池管理,控制其釋放的時機众旗。
這個時候也許大家會想到一個在面試的時候經(jīng)常遇到的一個面試題:當我們需要在for循環(huán)里面循環(huán)無限次創(chuàng)建對象,怎么避免內(nèi)存的激增?
當我們使用for循環(huán)創(chuàng)建很多個使用autorelease方式創(chuàng)建的NSString對象的時候,將所有的對象的釋放權(quán)都交給了RunLoop 的釋放池,而RunLoop的釋放池會等待這個事件處理之后才會釋放筒愚,因此就會使對象無法及時釋放,堆積在內(nèi)存造成內(nèi)存泄露菩浙,可以在Debug Navigation 中觀察到內(nèi)存激增巢掺。為了驗證確實是因為autorelease這種創(chuàng)建方式引起的內(nèi)存泄露,我做了如下的測試:
int largeNumber = 100000000;
- (void)correctSolution1{
for (int i = 0; i < largeNumber; i++) {
NSString *str = [[NSString alloc] initWithFormat:@"hello -%04d", i];
str = [str stringByAppendingString:@" - world"];
}
}
// stringWithFormat:本質(zhì)上是調(diào)用了alloc + initWithFormat: + autorelease
// 我在本例中將stringWithFormat:方法換成了alloc + initWithFormat:
// 這樣做問題就解決了:內(nèi)存幾乎沒有變化劲蜻。反向驗證了內(nèi)存飆升確實是autorelease創(chuàng)建方式造成的陆淀。
但是在編寫代碼的時候我們?nèi)匀涣?xí)慣用類的快速創(chuàng)建方法,而不是alloc+init斋竞。也就是說倔约,為了方便寫程序,又使用了底層實現(xiàn)是alloc+init+autorelease的快速創(chuàng)建對象的方法(如 stringWithFormat:)坝初。因此解決的方案就是添加局部的釋放池浸剩,以及時釋放內(nèi)存钾军,如果將局部釋放池添加到循環(huán)外:
- (void)wrongSolution{
@autoreleasepool {
for (int i = 0; i < largeNumber; i++) {
NSString *str = [NSString stringWithFormat:@"hello -%04d", i];
str = [str stringByAppendingString:@" - world"];
}
}
}
這樣顯然是沒有效果的,釋放池需要等循環(huán)執(zhí)行之后再釋放內(nèi)存绢要,這和使用RunLoop創(chuàng)建的釋放池沒有什么區(qū)別吏恭。 較好的方案就是每次循環(huán)的時候添加一個釋放池:
- (void)correctSolution2{
for (int i = 0; i < largeNumber; i++) {
@autoreleasepool {
NSString *str = [NSString stringWithFormat:@"hello -%04d", i];
str = [str stringByAppendingString:@" - world"];
}
}
}
這樣每一次循環(huán)的結(jié)束時都會釋放一次內(nèi)存,因而這個循環(huán)全部執(zhí)行完成時也幾乎不消耗內(nèi)存重罪。
總結(jié)是:
做多線程開發(fā)時,需要在線程調(diào)度方法中手動添加自動釋放池樱哼,尤其是當執(zhí)行循環(huán)的時候,如果循環(huán)內(nèi)部有使用類的快速創(chuàng)建方法創(chuàng)建的對象剿配, 一定要將循環(huán)體放到自動釋放池中搅幅。