使用自动释放池
自动释放池块提供了一种机制,您可以放弃对对象的持有,但避免立即将其释放的可能性(例如,当您从方法返回对象时)。通常,不需要创建自己的自动释放池块,但在某些情况下,必须这样做,或者这样做是有益的。
关于自动释放池
使用@autoreleasepool标记自动释放池块,如以下示例所示:
@autoreleasepool {
// Code that creates autoreleased objects.
}
在自动释放池块的末尾,在块内收到自动释放消息的对象将被发送release消息——对象每次在块内发送autorelease消息时都会收到release消息。
像任何其他代码块一样,自动释放池块可以嵌套:
@autoreleasepool {
// . . .
@autoreleasepool {
// . . .
}
. . .
}
(您通常不会完全看到上述代码;通常,一个源文件中的自动发布池块中的代码会调用另一个自动发布池块中包含的另一个源文件中的代码。)对于给定的autorelease消息,相应的release消息在发送autorelease消息的自动发布池块的末尾发送。
Cocoa总是希望代码在自动释放池块内执行,否则自动释放的对象不会被释放,应用程序会泄漏内存。(如果您在自动释放池块之外发送自动释放消息,Cocoa会记录一个合适的错误消息。)AppKit和UIKit框架在自动释放池块中处理每个事件循环迭代(例如鼠标向下事件或点击)。因此,您通常不必自己创建自动释放池块,甚至不必查看用于创建池块的代码。然而,有三种情况下,您可能会使用自己的自动释放池块:
-
如果您正在编写一个不基于UI框架的程序,例如命令行工具。
-
如果你写一个创建许多临时对象的循环。
您可以在循环中使用自动释放池块在下一次迭代之前处理这些对象。在循环中使用自动释放池块有助于减少应用程序的最大内存占用。
- 如果您生成了一个次要线程。
您必须在线程开始执行时立即创建自己的自动释放池块;否则,您的应用程序将泄露对象。(有关详细信息,请参阅自动释放池块和线程。)
使用本地自动释放池块来减少峰值内存占用空间
许多程序会创建自动释放的临时对象。这些对象直到块结束会增加程序的内存占用。在许多情况下,允许临时对象累积到当前事件循环迭代结束不会导致过多的开销;但是,在某些情况下,您可能会创建大量临时对象,这些对象会大幅增加内存占用空间,并且您希望更快地处理这些对象。在后一种情况下,您可以创建自己的自动释放池块。在块的末尾,临时对象被释放,这通常会导致它们dealloc,从而减少程序的内存占用。
以下示例展示了如何在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循环一次处理一个文件。在自动释放池块内发送自动释放消息的任何对象(如文件内容)都会在块末尾释放。
在自动释放池块之后,您应该将块内自动释放的任何对象视为“已禁用”。不要向该对象发送消息或将其返回给方法的调用者。如果您必须在自动释放池块之外使用临时对象,您可以通过向块内的对象发送retain消息,然后在块后发送自动释放,如本示例所示:
– (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消息,并在自动释放池块延长match寿命后向其发送自动释放,并允许它在循环之外接收消息并返回给findMatchingObject的调用者:
自动释放池块和线程
Cocoa应用程序中的每个线程都维护自己的自动释放池块堆栈。如果您正在编写仅限基础的程序,或者如果您分离线程,则需要创建自己的自动发布池块。
如果您的应用程序或线程寿命长,并可能生成大量自动释放对象,您应该使用自动释放池块(如AppKit和UIKit在主线程上所做的那样);否则,自动释放对象会累积,您的内存占用也会增加。如果您的分离线程不进行Cocoa调用,则无需使用自动释放池块。
注意:如果您使用POSIX线程API而不是NSThread创建辅助线程,除非Cocoa处于多线程模式,否则无法使用Cocoa。Cocoa只有在分离其第一个NSThread对象后才能进入多线程模式。要在次要POSIX线程上使用Cocoa,您的应用程序必须首先至少分离一个NSThread对象,该对象可以立即退出。您可以使用NSThread类方法isMultiThreaded测试Cocoa是否处于多线程模式。