在大多数情况下使用ARC(自动引用计数),我们不需要考虑使用Objective-C对象的内存管理 . 不允许再创建 NSAutoreleasePool
,但是有一个新的语法:
@autoreleasepool {
…
}
我的问题是,当我不应该手动释放/自动释放时,为什么我需要这个呢?
EDIT: 总结我从所有的答案和评论中得到的简洁:
New Syntax:
@autoreleasepool { … }
是新的语法
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
…
[pool drain];
More importantly:
-
ARC使用
autorelease
以及release
. -
它需要一个自动释放池才能这样做 .
-
ARC不会为您创建自动释放池 . 然而:
-
每个Cocoa应用程序的主线程中都有一个自动释放池 .
-
有两种情况可能需要使用
@autoreleasepool
: -
当您处于辅助线程且没有自动释放池时,您必须自己制作以防止泄漏,例如
myRunLoop(…) { @autoreleasepool { … } return success; }
. -
当你想创建一个更本地的游泳池时,正如@mattjgalloway在他的回答中所示 .
7 回答
ARC没有摆脱保留,发布和自动释放,它只是为您添加所需的 . 所以仍有调用保留,仍有调用释放,仍有调用自动释放,仍有自动释放池 .
他们使用新的Clang 3.0编译器和ARC进行的其他一项更改是,他们用
@autoreleasepool
编译器指令替换了NSAutoReleasePool
.NSAutoReleasePool
无论如何总是有点特殊"object"并且它们使得使用它的语法不与对象混淆,因此它通常更简单一些 .所以基本上,你需要
@autoreleasepool
因为还有自动发布池需要担心 . 你只需要担心添加autorelease
调用 .使用自动释放池的示例:
一个非常人为的例子,当然,如果你没有
@autoreleasepool
在外部for
-loop中,那么你将在以后释放100000000个对象,而不是每次围绕外部for
-loop释放10000个 .Update: 另请参阅此答案 - https://stackoverflow.com/a/7950636/1068248 - 为什么
@autoreleasepool
与ARC无关 .Update: 我看了一下这里发生了什么的内部和wrote it up on my blog . 如果您查看那里,那么您将看到ARC正在做什么,以及编译器如何使用新样式
@autoreleasepool
以及它如何引入范围来推断需要保留,释放和自动释放的信息 .@autoreleasepool doesn 't autorelease anything. It creates an autorelease pool, so that when the end of block is reached, any objects that were autoreleased by ARC while the block was active will be sent release messages. Apple' s Advanced Memory Management Programming Guide解释如此:
人们经常误解ARC进行某种垃圾收集等 . 事实是,过了一段时间,Apple的人们(感谢llvm和clang项目)意识到Objective-C的内存管理(所有
retains
和releases
等)可以在编译时完全自动化 . 这就是通过阅读代码,甚至在它运行之前! :)为了做到这一点,只有一个条件:我们必须遵循rules,否则编译器将无法在编译时自动化该过程 . 因此,为了确保我们违反规则,我们不允许显式写入
release
,retain
等 . 这些调用由编译器自动注入到我们的代码中 . 因此在内部我们仍然有autorelease
s,retain
,release
等 . 我们不需要再写它们了 .ARC的A在编译时是自动的,这比在垃圾收集时的运行时要好得多 .
我们仍然有
@autoreleasepool{...}
因为它没有违反任何规则,我们可以随时创建/消耗我们的池我们需要它:) .这是因为你仍然需要为编译器提供关于何时安全自动释放的对象超出范围的提示 .
引自https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmAutoreleasePools.html:
...
从方法返回新创建的对象需要自动释放池 . 例如 . 考虑这段代码:
方法中创建的字符串的保留计数为1 . 现在谁来 balancer 保留计数与释放?
方法本身?不可能,它必须返回创建的对象,因此在返回之前不得释放它 .
方法的调用者?调用者不希望检索需要释放的对象,方法名称并不意味着创建了一个新对象,它只表示返回一个对象,而这个返回的对象可能是一个需要释放的新对象,但它可能是很好是现有的没有 . 该方法返回的内容甚至可能取决于某些内部状态,因此调用者无法知道是否必须释放该对象并且它不应该关心 .
如果调用者必须总是按照约定释放所有返回的对象,那么在从方法返回之前,总是必须保留每个新创建的对象,并且一旦调用者超出范围就必须释放它,除非它又被退回了 . 在许多情况下,这将是非常低效的,因为如果调用者不总是释放返回的对象,则在许多情况下可以完全避免改变保留计数 .
这就是为什么有自动释放池,所以第一种方法实际上会成为
在一个对象上调用
autorelease
会将它添加到自动释放池中,但是这对于自动释放池添加一个对象到底意味着什么呢?嗯,这意味着告诉你的系统“我希望你为我释放那个对象但是在以后的某个时间,而不是现在;它有一个需要通过释放来 balancer 的保留计数,否则内存会泄漏但是我不能自己做现在,因为我需要对象保持活动超出我当前的范围,我的调用者也不会为我做这件事,它不知道这需要做 . 所以把它添加到你的池中,一旦你清理了游泳池,也为我清理我的物体 . “使用ARC,编译器会决定何时保留对象,何时释放对象以及何时将其添加到自动释放池,但仍需要存在自动释放池才能从方法中返回新创建的对象而不会泄漏内存 . Apple刚刚对生成的代码进行了一些非常好的优化,有时会在运行时消除自动释放池 . 这些优化要求调用者和被调用者都使用ARC(记住混合ARC和非ARC是合法的并且也是正式支持的),如果实际上是这种情况,则只能在运行时知道 .
考虑这个ARC代码:
系统生成的代码可以像下面的代码一样(这是允许您自由混合ARC和非ARC代码的安全版本):
(注意调用者中的保留/释放只是一个防御性的安全保留,它不是严格要求的,如果没有它,代码将是完全正确的)
或者它的行为类似于此代码,以防两者都在运行时检测到使用ARC:
正如您所看到的,Apple消除了atuorelease,因此也消除了池被破坏时延迟的对象释放,以及安全保留 . 要了解更多关于可能性和真实性的信息在幕后进行,check out this blog post.
现在回答实际问题:为什么要使用
@autoreleasepool
?对于大多数开发人员来说,今天只有一个原因是在他们的代码中使用这个构造,并且在适用的情况下保持较小的内存占用 . 例如 . 考虑这个循环:
假设每次调用
tempObjectForData
都可以创建一个返回自动释放的新TempObject
. for循环将创建一百万个这些临时对象,这些临时对象都在当前自动释放池中收集,并且只有在该池被销毁时,所有临时对象也会被销毁 . 在此之前,您在内存中有一百万个这些临时对象 .如果您编写这样的代码:
然后每次for循环运行时都会创建一个新池,并在每次循环迭代结束时销毁 . 这样,尽管循环运行了一百万次,但最多一个临时对象随时都会在内存中闲置 .
在过去,您经常必须在管理线程时自己管理自动释放池(例如使用
NSThread
),因为只有主线程自动拥有Cocoa / UIKit应用程序的自动释放池 . 然而今天这几乎是遗产,因为今天你可能不会使用GCDDispatchQueue
或NSOperationQueue
,这两个都会为你管理一个顶级自动释放池,在运行块/任务之前创建并在完成后销毁 .关于这个话题似乎有很多混淆(至少有80个人可能现在对此感到困惑,并认为他们需要在他们的代码周围撒上@autoreleasepool) .
如果一个项目(包括它的依赖项)专门使用ARC,那么@autoreleasepool永远不需要使用,也不会有用 . ARC将在正确的时间处理释放对象 . 例如:
显示:
一旦值超出范围,就会释放每个Testing对象,而不等待退出自动释放池 . (同样的事情发生在NSNumber示例中;这只是让我们观察dealloc . )ARC不使用自动释放 .
@autoreleasepool仍然允许的原因是混合ARC和非ARC项目,它们尚未完全转换为ARC .
如果调用非ARC代码,它可能会返回一个自动释放的对象 . 在这种情况下,上面的循环会泄漏,因为永远不会退出当前的自动释放池 . 那个's where you'd想在代码块周围放一个@autoreleasepool .
但如果你完全完成了ARC转换,那么就忘掉autoreleasepool了 .