NSAutoreleasePool自动释放池如何工作?

时间:2008-09-15 18:27:34

标签: objective-c memory-management nsautoreleasepool foundationkit

据我所知,任何使用 alloc new copy 创建的内容都需要手动发布。例如:

int main(void) {
    NSString *string;
    string = [[NSString alloc] init];
    /* use the string */
    [string release];
}

我的问题是,这不是有效吗?:

int main(void) {
    NSAutoreleasePool *pool;
    pool = [[NSAutoreleasePool alloc] init];
    NSString *string;
    string = [[[NSString alloc] init] autorelease];
    /* use the string */
    [pool drain];
}

7 个答案:

答案 0 :(得分:65)

是的,你的第二个代码snippit是完全有效的。

每次--autorelease被发送到一个对象,它被添加到最里面的自动释放池。当池被耗尽时,它只是发送-release给池中的所有对象。

自动释放池只是一种便利,允许您推迟发送 - 直到“以后”。 “稍后”可能会在多个地方发生,但Cocoa GUI应用程序中最常见的是在当前运行循环周期结束时。

答案 1 :(得分:37)

NSAutoreleasePool:排水与释放

由于drainrelease的功能似乎引起了混淆,因此在此可能值得澄清(尽管the documentation已涵盖这一点......)。

严格来说,从大图来看,drain 等同于release

在引用计数环境中,drain执行与release相同的操作,因此两者在这种意义上是等效的。要强调,这意味着如果您使用drain而不是release,则会泄漏池。

在垃圾收集环境中,release是无操作的。因此它没有效果。另一方面,drain包含一个收集器的提示,它应该“在需要时收集”。因此,在垃圾收集环境中,使用drain有助于系统平衡收集扫描。

答案 2 :(得分:17)

正如已经指出的那样,您的第二个代码段是正确的。

我想建议一种更简洁的方法来使用适用于所有环境的自动释放池(引用计数,GC,ARC),并避免排放/释放混淆:

int main(void) {
  @autoreleasepool {
    NSString *string;
    string = [[[NSString alloc] init] autorelease];
    /* use the string */
  }
}

在上面的示例中,请注意 @autoreleasepool 块。记录在案here

答案 3 :(得分:7)

不,你错了。文档清楚地表明,在非GC下,-drain相当于-release,这意味着NSAutoreleasePool将泄露。

答案 4 :(得分:0)

我从Apple那里读到的内容: “在自动释放池块的末尾,在块中接收到自动释放消息的对象被发送一个释放消息 - 一个对象在每次在块中发送自动释放消息时都会收到释放消息。”

https://developer.apple.com/library/mac/documentation/cocoa/conceptual/MemoryMgmt/Articles/mmAutoreleasePools.html

答案 5 :(得分:0)

发送自动释放而不是释放到对象会延长该对象的生命周期,至少直到池本身耗尽(如果随后保留该对象,它可能会更长)。一个对象可以多次放入同一个池中,在这种情况下,每次将它放入池中时都会收到一条释放消息。

答案 6 :(得分:-2)

是和否。你最终会释放字符串内存但是如果你在垃圾收集(非内存管理)环境下运行它,则使用drain而不是release来将NSAutoreleasePool对象“泄漏”到内存中。这种“泄漏”只会使NSAutoreleasePool的实例“无法访问”,就像在GC下没有强指针的任何其他对象一样,并且该对象将在下次GC运行时被清除,这很可能是在调用{{1 }}:

  

漏极

     

在垃圾收集环境中,如果自上次收集后分配的内存大于当前阈值,则触发垃圾收集;否则表现为释放。   ...   在垃圾收集环境中,此方法最终调用-drain

否则,它类似于objc_collect_if_needed在非GC下的行为,是的。正如其他人所说,-release是GC下的无操作,因此确保池在GC下正常运行的唯一方法是通过-release-drain非GC工作与非GC下的-drain完全相同,并且可以说更清楚地传达其功能。

我应该指出你的语句“用new,alloc或init调用的任何东西”不应该包含“init”(但应该包括“copy”),因为“init”不分配内存,它只设置object(构造函数方式)。如果你收到一个alloc'd对象而你的函数只调用了init,你就不会释放它:

-release

它不会消耗比你已经开始使用的内存更多的内存(假设init没有实例化对象,但你无论如何都不负责)。