NSAutoreleasePool自动释放池如何工作?


95

据我了解,使用allocnewcopy创建的任何内容都需要手动释放。例如:

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];
}

Answers:


68

是的,您的第二个代码段是完全有效的。

每次将-autorelease发送给对象时,都会将其添加到最内部的autorelease池中。当池耗尽时,它仅向池中的所有对象发送-release。

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


5
如果我没有循环,当前运行循环周期的结束在哪里?
感谢

24
“最外面”不应该是“最里面”吗?
Mike Weller 2010年

an object应该是an object that is a subclass of NSObject or NSProxy and doesn't override -autorelease

1
编辑:从最外层更改为最内层。
chakrit 2011年

1
重要说明:如果使用自动引用计数(ARC),则不能直接使用自动释放池。而是使用@autoreleasepool块。从developer.apple.com/library/mac/#documentation/Cocoa/Reference/...
马里马比尔·拉曼

37

NSAutoreleasePool:消耗与释放

由于的功能drainrelease似乎引起混乱,因此在这里可能需要澄清(尽管在文档中已对此进行了介绍)。

严格地说,从大局的角度drain等同于release

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

在垃圾收集的环境中,release禁止操作。因此,它没有任何作用。 drain另一方面,向收集器暗示它应该“根据需要收集”。因此,在垃圾收集的环境中,使用drain有助于系统平衡收集扫描。


4
从根本上讲,“泄漏” a是不可能的NSAutoreleasePool。这是因为池像堆栈一样运行。实例化池会将池推到线程自动释放池堆栈的顶部。 -release导致该池从栈中弹出被推在它上面的任何池,但由于某种原因没有弹出。
约翰

7
这与我写的内容有什么关系?
mmalc

2
我喜欢他如何花时间进行大胆的AND操作。快点!
比利·格雷

17

如前所述,您的第二个代码段是正确的。

我想提出一种更简洁的方法来使用在所有环境(引用计数,GC,ARC)上都可以使用的自动释放池,并避免浪费/释放混乱:

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

在上面的示例中,请注意@autoreleasepool块。这在此处记录


2
请注意,ARC不允许自动释放。
dmirkitanov 2012年

1
为了明确起见,必须将该@autoreleasepool块与ARC 一起使用。
西蒙(Simon)



0

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


-2

是的,没有。您最终将释放字符串内存,但是如果在垃圾回收(不是内存托管)环境下运行NSAutoreleasePool对象,则使用排水而不是释放将NSAutoreleasePool对象泄漏到内存中。此“泄漏”仅会使NSAutoreleasePool的实例像其他任何在GC下没有强指针的对象一样“不可访问”,并且该对象将在下一次GC运行时被清除,这很可能直接在调用之后-drain

排水

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

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

我应该指出,您的语句“用new,alloc或init调用的任何内容”都不应包含“ init”(但应包含“ copy”),因为“ init”不会分配内存,它只会设置对象(构造函数)时尚)。如果收到分配对象,并且函数仅这样调用init,则不会释放该对象:

- (void)func:(NSObject*)allocd_but_not_init
{
    [allocd_but_not_init init];
}

那不会消耗比您开始时更多的内存(假设init不会实例化对象,但是无论如何您都不会对这些对象负责)。


当您关于流失的信息不太正确时,我不愿意将此答案保留为接受状态。请参阅developer.apple.com/documentation/Cocoa/Reference/Foundation/…更新,我将重新接受。
詹姆斯·萨姆纳斯

什么是不正确的答复?在垃圾收集的环境中(如上所述),排水不会删除AutoReleasePool,因此除非使用release,否则您泄漏内存。我列出的报价是从马口中直接得出来的,文件正在流失。
洛伦·西格尔

1
洛伦:在GC下,-[NSAutoreleasePoolrain]将触发收集。-retain,-release和-autorelease都被收集器忽略;这就是在GC下在自动释放池上使用-drain的原因。
克里斯·汉森

在“排水”文档中:在托管内存环境中,此行为与调用release相同。因此,如果您使用'drain'而不是release,则不会泄漏内存。
mmalc

-[NSAutoreleasePool release]在垃圾收集的环境中是不可操作的。-[NSAutoreleasePool drain]在引用计数和垃圾回收环境中均可使用。
乔纳森·斯特林
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.