8

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

我很Objective-C陌生,需要一些帮助来弄清楚我的记忆发生了什么。我已经缩小了问题的范围,以解释为什么以下基本代码的行为如此。

此代码添加到 Xcode 提供的基于 Cocoa 的普通应用程序模板中(启用了 ARC)。

案例A

- (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. [NSDate date]在 ARC 下,使用“autoreleased”和 alloc init 对象有什么区别?(我认为这里的其他问题几乎没有。)
  2. 为什么运行代码时 ARC 似乎没有启动?
  3. 为什么分析的应用程序和实际应用程序的内存行为存在差异?
4

2 回答 2

4

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

这种行为会导致您的对象在整个循环中保留在内存中,以防它们被自动释放以供稍后释放,而如果您alloc/init它们,则循环体内release发送一个方法。

@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.

于 2013-06-20T08:53:32.067 回答
2

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

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

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

于 2013-06-20T08:36:24.233 回答