为什么 ARC 在使用 [NSDate 日期] 时不释放内存,但使用 [[NSDate 分配] init] 工作正常?



我编写了一些Objective-C应用程序,除了内存占用不断增加之外,它似乎工作得很好。我在Xcode 4.6.2的最后一个版本下使用ARC。我的系统是10.7.5。

我对Objective-C很陌生,需要一些帮助来弄清楚我的记忆是怎么回事。我已经把问题缩小到解释为什么下面这段基本代码的行为是这样的。

这段代码被添加到Xcode给出的香草可可应用程序模板中(启用了ARC)。

情况下

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification{
    NSDate* d;
    for(int i=0; i<1000000; i++){
        d = [[NSDate alloc] init];
    }
}

一切都如预期的那样,ARC在运行中回收内存。也就是说,内存使用历史是非常平坦的。当for循环结束时,应用程序占用约25MB内存。

案例B

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification{
    NSDate* d;
    for(int i=0; i<1000000; i++){
        d = [NSDate date];
    }
}
这里的事情对我来说真的很神秘。当只是运行应用程序时,(实际)内存使用量不断增加到大约53MB,然后永远保持在那里。

然而,当运行分配分析器工具时,我可以看到,在for循环结束时,所有对象都被释放,非常像您期望的自动释放池。此外,用@autoreleasepool{}封装for循环的体使得情况B的行为类似于情况A(如预期的那样)。

那么,三个问题:

  1. 在ARC下,使用"自动释放"[NSDate date]和alloc init对象之间的区别是什么?(我以为这里几乎没有其他问题。)
  2. 为什么在运行代码时ARC似乎没有启动?
  3. 为什么在被分析的应用程序和实际应用程序的内存行为中存在差异?

自动释放对象最终被释放,而在alloc/init的情况下,它们在不再使用时被释放。

这种行为会导致你的对象在整个循环中都保持在内存中,以免它们被自动释放,而如果你alloc/init它们,一个release方法会在循环体中发送

您可以很容易地使body循环节省内存,通过将其包装在@autoreleasepool中,如下所示:

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification{
    @autoreleasepool {
        NSDate* d;
        for(int i=0; i<1000000; i++){
            d = [NSDate date];
        }
    }
}

这将给ARC一个提示,表明你想在每次循环迭代时创建并释放一个自动释放池。

在特定情况下,最简单的选择可能是使用alloc/init方法,以便编译器自动做正确的事情,但如果你有一个循环体,有许多返回自动释放实例的工厂方法,@autoreleasepool块可能是一个很好的方法。

作为最后的注释,@autoreleasepool不是ARC独有的。它从LLVM 3.0开始就存在了,有了足够多的现代目标(即iOS5和OSX 10.7),它比老式的NSAutoreleasePool快得多。

[NSDate date]正在创建一个自动释放对象,该对象将在下次程序进入事件循环时被释放。

另一种情况由ARC在循环中释放。

如果你真的想做这样的事情,你可以创建你自己的自动释放池,并定期释放它。例如Objective-C:为什么ARC仍然需要autorelease (@autoreleasepool) ?

最新更新