天天看點

了解自動釋放池

首先來段官方文檔:

Important

If you use Automatic Reference Counting (ARC), you cannot use autorelease pools directly. Instead, you use 

@autoreleasepool

 blocks. For example, in place of:

Code Listing 1

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// Code benefitting from a local autorelease pool.
[pool release];
           

you would write:

Code Listing 2

@autoreleasepool {
    // Code benefitting from a local autorelease pool.
}
           

@autoreleasepool

 blocks are more efficient than using an instance of 

NSAutoreleasePool

 directly; you can also use them even if you do not use ARC.

大家都知道,自動釋放池是用來存放那些需要在稍後某個時刻釋放的對象,清空(drain)自動釋放池時,系統會向其中的對象發送release消息。

在使用的過程中,你有沒有下面這些疑問:

問題1:

都知道程式入口main會自動建立一個自動釋放池,如下:

int main(int argc, char * argv[]) {
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}
           

這個塊的末尾就是應用程式的終止處,這個時候作業系統就會把程式所占有的全部記憶體釋放掉,還要自動釋放池做甚?

問題2:

那些程式中加入到1中池子裡面的自動釋放的對象難道也是這個時候才被銷毀,那和記憶體洩漏有啥差別啊?

問題3:

什麼時候用池來優化效率

問題4:

自動釋放池什麼時候被釋放

問題一一解答:

問題1:

如果不寫這個塊的話,那麼由UIApplicationMain函數所自動釋放的那些對象,就沒有自動釋放池可以容納了,于是系統會發出警告資訊,是以說,這個池可以了解成最外圍捕捉全部自動釋放對象所用的池。

問題2:

以下是蘋果官方文檔

The Application Kit creates an autorelease pool on the main thread at the beginning of every cycle of the event loop, and drains it at the end, thereby releasing any autoreleased objects generated while processing an event. If you use the Application Kit, you therefore typically don’t have to create your own pools. If your application creates a lot of temporary autoreleased objects within the event loop, however, it may be beneficial to create “local” autorelease pools to help to minimize the peak memory footprint.

這裡的大概意思就是說,應用程式庫會在主線程的每個事件循環周期的開始建立一個自動釋放池,并在結束的時候清空它,進而在處理一個事件的時候釋放掉所有之前生成的自動釋放的對象;如果你使用應用程式庫,你通常不需要自己來建立你自己的池。但是,如果你的應用在應用循環周期内建立很多臨時的自動釋放對象,建立“本地”自動釋放池可能有利于幫助減少峰值記憶體占用。

舉個栗子:

for (int i = 0; i < 10000; i++) {
        NSMutableArray *mArray = [NSMutableArray array];
        [mArray addObject:@"temp"];
    }
           

分析:上面的例子中,建立了很多臨時數組對象,可是每次循環完以後,它們都不會再使用了,但是它們依然處于存活狀态,目前還在釋放池中,等待系統稍後将其釋放并回收。然而,自動釋放池要等線程執行下一次事件循環時才會清空。這就意味着在執行for循環時,會持續有新對象建立出來,并加入自動釋放池,所有這種對象都要等for循環執行完才會釋放,這樣一來,在執行for循環時,應用程式所占記憶體量就會持續上漲,而等到所有臨時對象釋放後,記憶體用量又會突然下降。

解決方法:如下,很明顯,不用再分析了。

for (int i = 0; i < 10000; i++) {
        @autoreleasepool{
            NSMutableArray *mArray = [NSMutableArray array];
            [mArray addObject:@"temp"];
        }
    }
           

這裡還要注意的是Mac OS X和iOS應用程式分别運作于Cocoa和Cocoa Touch環境,系統會自動建立一些線程,比如說主線程或是“大中樞派發”(Grand Central Dispatch,GCD)機制中的線程,這些線程預設都有自動釋放池,每次執行"事件循環"(event loop)時,就會将其清空。是以,不需要自己來建立自動釋放池塊。

問題3:

盡管自動釋放池塊的開銷不太大,但是畢竟還是有的,是以盡量不要建立額外的自動釋放池,優化之前可以先監控記憶體用量,看是否需要。

問題4:

以下是蘋果官方文檔

You create an 

NSAutoreleasePool

 object with the usual 

alloc

 and 

init

 messages and dispose of it with 

drain

 (or 

release

—to understand the difference, see Garbage Collection). Sinceyou cannot retain an autorelease pool (or autorelease it—see 

retain

 and 

autorelease

),draining a pool ultimately has the effect of deallocating it.You should always drain an autorelease pool in the same context (invocation of a method or function, or body of a loop) that it was created. See Using Autorelease Pool Blocks for more details.

Each thread (including the main thread) maintains its own stack of 

NSAutoreleasePool

objects (see Threads). As new pools are created, they get added to the top of the stack. When pools are deallocated, they are removed from the stack. Autoreleased objects are placed into the top autorelease pool for the current thread. When a thread terminates,it automatically drains all of the autorelease pools associated with itself.

紅線部分就是答案,清空池最終會導緻銷毀,目前線程終止的時候會自動清空相關聯的所有的池。

這裡的大概意思就是說,每個線程包括主線程都保持它自己的自動釋放池的棧,當新的池被建立,它們被添加到棧的頂部,放池被銷毀,它們會從棧移除,自動釋放的對象被放在目前線程的最頂部的自動釋放池中,當線程被終止,它會自動清空所有與自己關聯的自動釋放池。

繼續閱讀