为什么在ARC中仍然需要@autoreleasepool?
对于大多数情况下,使用ARC(自动引用计数),我们不需要考虑Objective-C对象的内存pipe理。 不允许再创buildNSAutoreleasePool
,但有一个新的语法:
@autoreleasepool { … }
我的问题是,当我不应该手动释放/自动释放时,为什么我需要这个?
编辑:总结一下我所得到的一切和评论简洁:
新的语法:
@autoreleasepool { … }
是新的语法
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; … [pool drain];
更重要的是:
- ARC使用
autorelease
以及release
。 - 它需要一个自动释放池来实现。
- ARC不会为您创build自动释放池。 然而:
- 每个Cocoa应用程序的主线程中都有一个autorelease池。
- 有两种情况你可能想要使用
@autoreleasepool
:- 当你在一个辅助线程中,并且没有自动释放池时,你必须自己来防止泄漏,比如
myRunLoop(…) { @autoreleasepool { … } return success; }
myRunLoop(…) { @autoreleasepool { … } return success; }
。 - 当你想创build一个更加本地化的游泳池时,正如@mattjgalloway在他的回答中所表明的那样。
- 当你在一个辅助线程中,并且没有自动释放池时,你必须自己来防止泄漏,比如
ARC并没有摆脱保留,发布和autoreleases,它只是为您添加所需的。 所以仍然有保留呼叫,仍然有呼叫释放,仍然有电话autorelease,仍然有自动释放池。
他们使用新的Clang 3.0编译器和ARC所做的其他更改之一是用@autoreleasepool
编译器指令replace了@autoreleasepool
。 NSAutoReleasePool
总是有点特殊的“对象”,它们使得使用它的语法不会与对象混淆,所以它通常更简单一些。
所以基本上,你需要@autoreleasepool
因为仍然有自动释放池担心。 你只需要担心添加autorelease
调用。
使用自动释放池的示例:
- (void)useALoadOfNumbers { for (int j = 0; j < 10000; ++j) { @autoreleasepool { for (int i = 0; i < 10000; ++i) { NSNumber *number = [NSNumber numberWithInt:(i+j)]; NSLog(@"number = %p", number); } } } }
一个非常有@autoreleasepool
例子,当然,但是如果你没有在@autoreleasepool
的外部@autoreleasepool
for
那么你将会释放100000000个对象,而不是每次10000个对象。
更新:另请参阅此答案 – https://stackoverflow.com/a/7950636/1068248 – 为什么@autoreleasepool
与ARC无关。
更新:我看了一下这里正在发生的事情的内部,并在我的博客上写下来 。 如果你看看那里,那么你将会看到ARC究竟在做什么,以及编译器如何使用新的@autoreleasepool
风格以及它如何引入一个范围来推断关于什么是保留,释放和自动释放的信息。
@autoreleasepool
不autorelease任何东西。 它创build一个自动释放池,以便当到达块的末尾时,在块被激活时由ARC自动释放的任何对象将被发送释放消息。 Apple的高级内存pipe理编程指南解释如下:
在自动释放池块的末尾,在块内接收到自动释放消息的对象被发送释放消息 – 对象每次在块内发送自动释放消息时接收释放消息。
人们经常误解ARC进行某种垃圾收集等。 事实上,经过一段时间,苹果公司的人们(感谢llvm和clang项目)意识到Objective-C的内存pipe理(所有的retains
和releases
等)可以在编译时被完全自动化。 这是,只要读取代码,甚至在它运行之前! 🙂
为了做到这一点,只有一个条件:我们必须遵守规则 ,否则编译器将无法在编译时自动执行该过程。 因此,为了确保我们永远不会违反规则,我们不允许明确写入release
, retain
等。这些调用被编译器自动注入到我们的代码中。 因此,在内部,我们仍然有autorelease
, retain
, release
等,只是我们不需要再写了。
ARC的A在编译时是自动的,比运行时好得多,比如垃圾收集。
我们还有@autoreleasepool{...}
因为它没有违反任何规则,我们随时随地创build/stream失我们的池,我们需要它:)。
这是因为您仍然需要向编译器提供有关自动释放对象何时超出范围的安全提示。
自动释放池块和线程
Cocoa应用程序中的每个线程都维护自己的一堆autorelease池块。 如果你正在编写一个基金会的程序,或者如果你分离一个线程,你需要创build自己的autorelease池块。
如果您的应用程序或线程是长期的,并且可能会生成大量的自动释放对象,则应该使用自动释放池块(如主线程上的AppKit和UIKit)。 否则,自动释放的对象会累积并且内存占用量会增加。 如果你的分离的线程不做cocoa调用,你不需要使用自动释放池块。
注意:如果使用POSIX线程API而不是NSThread创build辅助线程,除非Cocoa处于multithreading模式,否则不能使用Cocoa。 cocoa进入multithreading模式后才分离其第一个NSThread对象。 要在辅助POSIX线程上使用Cocoa,应用程序必须首先分离至less一个可以立即退出的NSThread对象。 您可以使用NSThread类方法isMultiThreaded来testingCocoa是否处于multithreading模式。
…
在自动引用计数(ARC)中,系统使用与MRR相同的引用计数系统,但在编译时为您插入适当的内存pipe理方法调用。 强烈build议您将ARC用于新项目。 如果使用ARC,通常不需要理解本文档中描述的底层实现,尽pipe在某些情况下可能会有所帮助。 有关ARC的更多信息,请参阅转换到ARC版本说明。
在这个话题上似乎有很多混乱(至less有80人现在可能对此感到困惑,认为他们需要在他们的代码周围撒上@autoreleasepool)。
如果一个项目(包括它的依赖项)专门使用ARC,那么@autoreleasepool永远不需要使用,并且什么都不会有用。 ARC将在正确的时间处理释放对象。 例如:
@interface Testing: NSObject + (void) test; @end @implementation Testing - (void) dealloc { NSLog(@"dealloc"); } + (void) test { while(true) NSLog(@"p = %p", [Testing new]); } @end
显示:
p = 0x17696f80 dealloc p = 0x17570a90 dealloc
只要值超出范围,每个testing对象都将被释放,而不等待autorelease池退出。 (同样的事情发生在NSNumber例子中;这只是让我们观察dealloc。) ARC不使用autorelease。
@autoreleasepool仍然允许的原因是混合ARC和非ARC项目,这些项目还没有完全过渡到ARC。
如果你调用非ARC代码, 它可能会返回一个自动释放对象。 在这种情况下,上面的循环会泄漏,因为当前的自动释放池永远不会退出。 这就是你想要在代码块周围放置@autoreleasepool的地方。
但是,如果你已经完成了ARC转换,那么忘记autoreleasepool。