大家好,本文是文档翻译计划的第 04 篇,原文是:Using Autorelease Pool Blocks
自动释放池块提供了一种机制,您可以通过这种机制放弃对象的所有权,但是避免了立即释放它的可能性(例如,当您从一个方法返回一个对象时)。通常,您不需要创建自己的自动释放池块,但是在某些情况下,您必须这样做,或者这样做是有益的。
自动释放池块使用 @autoreleasepool 标记,如下面的示例所示:
@autoreleasepool {
// Code that creates autoreleased objects.
}
在 autorelease 池块的末尾,在块中接收到 autorelease 消息的对象将被发送一个 release 消息——每当对象在块中接收到一个 autorelease 消息时,它都会接收一个 release 消息。
和像其他的代码块一样,自动释放池块可以嵌套。
@autoreleasepool {
// . . .
@autoreleasepool {
// . . .
}
. . .
}
(通常不会看到与上面完全一样的代码。通常,一个源文件中的自动释放池块中的代码会调用另一个源文件中的代码,该源文件包含在另一个自动释放池块中。对于给定的 autorelease 消息,在发送 autorelease 消息的自动发布池块的末尾发送相应的 release 消息。
Cocoa总是期望代码在一个自动释放池块中执行,否则自动释放的对象不会被释放,你的应用程序会泄漏内存。
(如果您在 autorelease 池块之外发送一个 autorelease 消息,Cocoa 会打印一个适当的error log。)
AppKit 和 UIKit 框架处理一个自动释放池块中的每个事件循环迭代(例如一个鼠标向下事件或一个点击)。因此,您通常不必自己创建一个自动释放池块,甚至不必查看用于创建该块的代码。然而,在三种情况下,您可能会使用自己的自动释放池块:
许多程序都创建了自动释放的临时对象。这些对象会增加程序的内存占用,直到自动释放池块结束。在许多情况下,允许临时对象累积到当前事件循环迭代结束时,不会导致过多的开销;然而,在某些情况下,您可能会创建大量临时对象,这些临时对象会大量增加内存占用,您希望更快地处理它们。在后一种情况下,您可以创建自己的自动释放池块。在块的末尾,临时对象被释放,这通常会导致释放它们,从而减少程序的内存占用。
下面的示例演示如何在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 循环一次处理一个文件。在 autorelease 池块内发送自动释放消息的任何对象(例如fileContents)都会在块的末尾被释放。
在自动释放池块之后,您应该将块中自动释放的任何对象视为“已释放”。不要向该对象发送消息或将其返回给方法的调用者。如果你需要某个临时对象在自动释放池块结束之后依然可用,你可以通过发送一个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 的生命周期,所以可以在自动释放池以外继续存活,并且可以返回给 findMatchingObject 的调用者。