使用自动释放池块
自动释放池提供了一种“放弃对象所有权”的机制。这种机制可以避免对象被立即释放(比如:从函数内返回一个对象)。通常开发者不需要创建自己的自动释放池,但是,有些情况使用自动释放池,可以给你带来好处。
关于自动释放池块
自动释放池用@autoreleasepool来标识。如下所示:
@autoreleasepool {
// Code that creates autoreleased objects.
}
autorelease的对象,在自动释放池的最后,收到release消息。
(At the end of the autorelease pool block, objects that received an autorelease message within the block are sent a release message—an object receives a release message for each time it was sent an autorelease message within the block)
跟其他代码块一样,自动释放池可以嵌套
@autoreleasepool {
// . . .
@autoreleasepool {
// . . .
}
. . .
}
(你通常不会看到上面的代码;比较有代表性的是:包含自动释放代码的源文件,可能引用其他包含自动释放池代码的源文件),
对于一个autorelease对象,相应的release消息在“autorelease所在的自动释放池”的最后发送。
Cocoa 总是希望代码在自动释放池中执行,否则需要自动释放的对象将不会被释放掉,导致程序内存泄露。(如果你在自动释放池外发送一个autorelease消息,Cocoa log出一个相应的错误消息)
AppKit和UIKit框架已经处理了,每个“带有自动释放池的”事件循环迭代(比如鼠标按下,或者点击)。因此开发者无需创建一个自动释放池,可能你也看不到创建自动释放池的代码。
然而,有三种情况,开发者可能会使用自己的autorelease池:
● 不是基于UI架构的程序,比如命令行工具。
● 写一个用于创建很多临时变量的循环时候。
你可能在循环中使用一个自动释放池,在下次循环之前处理掉那些autorelease的对象。在循环中使用自动释放池块有助于减少应用程序占用的最大内存。
● 生成一个辅助线程。
开发者需要创建自己的自动释放池在线程开始执行前,否则,你的程序将会对象泄露(参考下面的 【自动释放池和线程】章节)
使用本地自动释放池,降低少内存占用峰值
很多程序创建autorelease的临时对象。这些对象一直占用内存直到自动释放池块结束。很多种情况,让临时对象在事件循环迭代中一直累加,不会造成过多的开销。然后在某些场景:你可能会创建大量的临时对象大大增加内存占用和你又想更迅速处理掉,对于后者,开发者可以创建自动释放池。
自动释放池结束时,临时对象被release掉,从而减少了该程序的内存占用。
下面例子展示怎样在for循环中使用一个自动释放池。
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. */
}
}
for循环一次处理一个文件,file contents 发送了autorelease消息,在在@autoreleasepool右大括号处被release。
经过自动释放池之后,开发者要认为所有对象已经被“处理了”,不要给其发消息,或者返回给调用者。如果开发者必须在自动释放池外用到临时对象,可以给对象retain消息,然后再发送autorelease消息。
如图所示:
– (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. */
}
对match变量发送的retain消息和在自动释放池后发送autorelease消息,是延长match变量的生命时间,让其在循环外接受消息,并返回给findMatchingObject的调用者。
自动释放池和线程
Cocoa程序中的每一个线程,都维护自己的自动释放池栈。如果你要写一个Foundation程序,或者卸载一个线程,你需要创建自己的autorelease池块。如果你的程序或者线程是常驻内存,并可能产生大量自动释放对象,你应该使用自动释放池(AppKit和UIKit在主线程中有自动释放池);否则自动释放对象累积,导致内存占用增长。
如果你不是Cocoa中卸载线程,你不需要使用一个自动释放池块。
【注意】
如果开发者使用POSIX(而不是NSTread)的API创建辅助线程,你不能使用Cocoa,除非Cocoa处于多线程模式。Cocoa只有卸载第一个NSTread对象之后,才进入多线程模式。
要想在辅助POSIX线程中使用Cocoa,你的程序必须首先卸载一个可以立即退出的NSTread对象。开发者可以通过调用isMultiThreaded函数,检测Cocoa是否处于多线程模式。