我已经编写了一些似乎工作正常的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(如预期的那样)。
所以,有三个问题:
[NSDate date]
和alloc init对象? (我认为这里的其他问题几乎没有。)答案 0 :(得分: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
快得多。
答案 1 :(得分:2)
[NSDate date]
正在创建一个自动释放的对象,该对象将在您的程序下次进入事件循环时释放。
另一种情况是由ARC在循环中释放的。
如果你真的想做这样的事情,你可以创建自己的自动释放池并定期排干它。请参阅示例Objective-C: Why is autorelease (@autoreleasepool) still needed with ARC?