关于C#:NSAutoreleasePool自动释放池如何工作?

关于C#:NSAutoreleasePool自动释放池如何工作?

How does the NSAutoreleasePool autorelease pool work?

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

1
2
3
4
5
6
int main(void) {
    NSString *string;
    string = [[NSString alloc] init];
    /* use the string */
    [string release];
}

我的问题是,这样难道不是同样有效吗?:

1
2
3
4
5
6
7
8
int main(void) {
    NSAutoreleasePool *pool;
    pool = [[NSAutoreleasePool alloc] init];
    NSString *string;
    string = [[[NSString alloc] init] autorelease];
    /* use the string */
    [pool drain];
}

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

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

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


NSAutoreleasePool:消耗与释放

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

严格来说,从大角度看,drain不等同于release

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

在垃圾回收的环境中,release是空操作。因此,它没有任何作用。另一方面,drain包含向收集器的提示,提示它应"根据需要收集"。因此,在垃圾回收的环境中,使用drain有助于系统平衡回收扫描。


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

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

1
2
3
4
5
6
7
int main(void) {
  @autoreleasepool {
    NSString *string;
    string = [[[NSString alloc] init] autorelease];
    /* use the string */
  }
}

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


不你错了。该文档明确指出,在非GC环境下,-drain等效于-release,这意味着NSAutoreleasePool将不会泄漏。


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


我从苹果那里读到的东西:
"在自动释放池块的末尾,向在该块内接收到自动释放消息的对象发送释放消息-每次在该块内向其发送自动释放消息时,对象都会收到释放消息。"

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


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

drain

In a garbage collected environment, triggers garbage collection if memory allocated since last collection is greater than the current threshold; otherwise behaves as release.
...
In a garbage-collected environment, this method ultimately calls objc_collect_if_needed.

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

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

1
2
3
4
- (void)func:(NSObject*)allocd_but_not_init
{
    [allocd_but_not_init init];
}

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


推荐阅读