自動釋放池block提供一種機制,你可以放棄一個對象的所有權,但避免立即被回收(例如當一個方法返回一個對象)。通常,你不需要創建自己的自動釋放池block,但也有一些情況下,你必須這么做,或者這么做是有益的。
關于自動釋放池block
使用 @autoreleasepool
標記自動釋放池block,見下面的例子:
<pre><code>
@autoreleasepool {
// Code that creates autoreleased objects.
}
</pre></code>
在自動釋放池block的結束,block中接收到autorelease
消息的對象會接收到release
消息,即接收到release
消息的對象在block中會受到autorelease
消息。
像其他的代碼block,自動釋放池block可以嵌套:
<pre><code>
@autoreleasepool {
// . . .
@autoreleasepool {
// . . .
}
. . .
}
</pre></code>
(通常你不會看到如上的代碼;通常一個源文件中的自動釋放池block中的代碼可能會調用另一個包含自動釋放池block的源文件。)對于一個給定autorelease
消息,相應的release
消息在自動釋放池block的結束發送autorelease
消息。
Cocoa希望代碼在自動釋放池block中執行,否則生成的對象不能被釋放,應用內存泄露。(如果你在自動釋放池block之外發送autorelease
消息,Cocoa會記錄錯誤消息。)AppKit和UIKit框架在自動釋放池block中處理每個事件循環迭代(如鼠標點擊事件)。因此,通常你不需要自己創建自動釋放池。然而,有三種情況你可能會使用自己的自動釋放池block:
- 如果你正在編寫不基于UI 框架的程序,比如命令行工具。
- 如果你編寫的循環創建了很多臨時對象。
你可以在循環中使用自動釋放池block,在下次迭代前處理這些對象。在循環中使用自動釋放池block,有助于減少應用程序的內存占用。
- 你生成了一個輔助線程。
一旦線程開始執行,你必須自己創建自動釋放池lock。否則,應用將泄漏對象。(詳情參見自動釋放池block和線程( Autorelease Pool Blocks and Threads))
使用本地自動釋放池block減少內存占用峰值
許多應用程序創建的臨時對象都是自動釋放的。這些對象添加到應用內存中直到block結束。在許多情況下,允許臨時對象積累到當前時間循環的最后,這樣不會導致過度開銷;然而,在某些情況,你可能會創建大量的臨時對象,大幅度增加內存占用,同時你希望能更快的處理。在后面這種情況,你可以自己創建自動釋放池block。在block的結束,臨時對象被釋放,這通常可以回收這些對象從而減少應用程序內存占用。
下面的例子展示了如何在for循環中使用本地自動釋放池block。
<pre><code>
NSArray *urls = <# An array of file URLs #>;
for (NSURL *url in urls) {
@autoreleasepool {
NSError *error;
NSString *fileContents = [NSString stringWithContentsOfURL:url
encoding:NSUTF8StringEncoding
error:&error];
/* Process the string, creating and autoreleasing more objects. */
}
}
</pre></code>
for循環一次處理一個文件。在自動釋放池block中的任何對象(例如,fileContents
)發送一個autorelease
消息,會在block結束被釋放。
在自動釋放池block后,可以認為對象都自動釋放。不要給該對象發送消息或返回方法調用。如果你必須在自動釋放池block之外使用臨時對象,可以在block中發送retain消息到對象,在block之后發送autorelease
消息,如本例所示:
<pre><code>
–(id)findMatchingObject:(id)anObject {
id match;
while (match == nil) {
@autoreleasepool {
/* Do a search that creates a lot of temporary objects. */
match = [self expensiveSearchForObject:anObject];
if (match != nil) {
[match retain]; /* Keep match around. */
}
}
}
return [match autorelease]; /* Let match go and return it. */
}
</pre></code>
在自動釋放池block中發送retain
消息到match
,在自動釋放池block拓展match
生命周期后發送autorelease
消息,并允許它接收循環外的消息,返回到調用findMatchingObject:
。
自動釋放池block和線程
Cocoa應用中的每個線程維護自己的自動釋放池block的堆棧。如果你正在編寫框架應用或你要分離線程,你需要自己創建自動釋放池block。
如果你的應用或線程是長期存在的并可能生成大量的自動釋放對象,你應該使用自動釋放池block(如AppKit和UIKit做主線程);否則,自動釋放對象積累,你的內存占用增加。如果Cocoa不調用你的分離線程,你不需要使用自動釋放池block。
注意:如果你使用POSIX線程API而不是
NSThread
創建輔助線程,不能使用Cocoa除非Cocoa處于多線程模式。Cocoa只有在分離第一個NSThread
對象后才會進入多線程模式。在輔助POSIX線程中使用Cocoa,你的應用程序首先分離至少在一個NSThread
對象,這個線程可以立即退出。你可以使用NSThread
類的isMultiThreaded
方法測試Cocoa是否處于多線程模式。