在大多数情况下,对于 ARC(自动引用计数),我们根本不需要考虑 Objective-C 对象的内存管理。不再允许创建 NSAutoreleasePool ,但是有一种新语法:
@autoreleasepool {
…
}
我的问题是,当我不应该手动释放/自动释放时,为什么我需要它?
编辑:简洁地总结我从所有答案和评论中得到的内容:
新语法:
@autoreleasepool { … }
是新语法
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
…
[pool drain];
更重要的是:
- ARC使用
autorelease
以及release
。
- 为此,它需要一个自动释放池。
- ARC 不会为您创建自动释放池。 但是:
- 每个 Cocoa 应用程序的主线程中都已经有一个自动释放池。
- 有两种情况您可能需要使用@autoreleasepool:
- 当你处于辅助线程并且没有自动释放池时,你必须自己创建一个以防止泄漏,例如
myRunLoop(...) { @autoreleasepool { ... } return success; }
。
- 当您希望创建一个更本地化的池时,正如 @mattjgalloway 在他的回答中所示。
For the most part with ARC (Automatic Reference Counting), we don't need to think about memory management at all with Objective-C objects. It is not permitted to create NSAutoreleasePool
s anymore, however there is a new syntax:
@autoreleasepool {
…
}
My question is, why would I ever need this when I'm not supposed to be manually releasing/autoreleasing ?
EDIT: To sum up what I got out of all the anwers and comments succinctly:
New Syntax:
@autoreleasepool { … }
is new syntax for
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
…
[pool drain];
More importantly:
- ARC uses
autorelease
as well as release
.
- It needs an auto release pool in place to do so.
- ARC doesn't create the auto release pool for you. However:
- The main thread of every Cocoa app already has an autorelease pool in it.
- There are two occasions when you might want to make use of
@autoreleasepool
:
- When you are in a secondary thread and there is no auto release pool, you must make your own to prevent leaks, such as
myRunLoop(…) { @autoreleasepool { … } return success; }
.
- When you wish to create a more local pool, as @mattjgalloway has shown in his answer.
发布评论
评论(8)
ARC 并没有去掉保留、发布和自动发布,它只是为您添加了所需的内容。因此仍然存在对保留的调用,仍然存在对释放的调用,仍然存在对自动释放的调用,并且仍然存在自动释放池。
他们对新的 Clang 3.0 编译器和 ARC 所做的其他更改之一是用
@autoreleasepool
编译器指令替换了NSAutoReleasePool
。 NSAutoReleasePool 无论如何总是有点特殊的“对象”,他们这样做是为了使使用它的语法不会与对象混淆,因此它通常更简单一些。所以基本上,您需要
@autoreleasepool
因为仍然需要担心自动释放池。您无需担心添加autorelease
调用。使用自动释放池的示例:
当然,这是一个精心设计的示例,但是如果您在外部
for
循环内没有@autoreleasepool
那么您就会稍后将释放 100000000 个对象,而不是每次外层for
循环释放 10000 个对象。更新:
另请参阅此答案 - https://stackoverflow.com/a/7950636/1068248 - 了解原因
@autoreleasepool
与 ARC 无关。更新:
我研究了这里发生的事情的内部情况和 把它写在我的博客上。如果您看一下那里,您将确切地看到 ARC 正在做什么,以及新样式 @autoreleasepool 以及它如何引入编译器如何使用范围来推断有关保留、释放和释放内容的信息。需要自动释放。
ARC doesn't get rid of retains, releases and autoreleases, it just adds in the required ones for you. So there are still calls to retain, there are still calls to release, there are still calls to autorelease and there are still auto release pools.
One of the other changes they made with the new Clang 3.0 compiler and ARC is that they replaced
NSAutoReleasePool
with the@autoreleasepool
compiler directive.NSAutoReleasePool
was always a bit of a special "object" anyway and they made it so that the syntax of using one is not confused with an object so that it's generally a bit more simple.So basically, you need
@autoreleasepool
because there are still auto release pools to worry about. You just don't need to worry about adding inautorelease
calls.An example of using an auto release pool:
A hugely contrived example, sure, but if you didn't have the
@autoreleasepool
inside the outerfor
-loop then you'd be releasing 100000000 objects later on rather than 10000 each time round the outerfor
-loop.Update:
Also see this answer - https://stackoverflow.com/a/7950636/1068248 - for why
@autoreleasepool
is nothing to do with ARC.Update:
I took a look into the internals of what's going on here and wrote it up on my blog. If you take a look there then you will see exactly what ARC is doing and how the new style
@autoreleasepool
and how it introduces a scope is used by the compiler to infer information about what retains, releases & autoreleases are required.@autoreleasepool
不会自动释放任何内容。它创建一个自动释放池,以便当到达块末尾时,在该块处于活动状态时由 ARC 自动释放的任何对象都将收到释放消息。 Apple 的高级内存管理编程指南如此解释:@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 explains it thus:人们经常将 ARC 误解为某种垃圾收集等。事实是,一段时间后,Apple 的人们(感谢 llvm 和 clang 项目)意识到 Objective-C 的内存管理(所有
保留
和释放
等)可以在编译时完全自动化。这就是,只需阅读代码,甚至在运行之前! :)为此,只有一个条件:我们必须遵循 规则,否则编译器将无法在编译时自动执行该过程。因此,为了确保我们永远不违反规则,我们不允许显式编写
release
、retain
等。这些调用会自动注入由编译器写入我们的代码。因此,我们内部仍然有autorelease
、retain
、release
等,只是我们不再需要编写它们了。ARC的A在编译时是自动的,这比像垃圾回收这样在运行时要好得多。
我们仍然有
@autoreleasepool{...}
因为拥有它不会违反任何规则,我们可以在需要时随时创建/耗尽我们的池:)。People often misunderstand ARC for some kind of garbage collection or the like. The truth is that, after some time people at Apple (thanks to llvm and clang projects) realized that Objective-C's memory administration (all the
retains
andreleases
, etc.) can be fully automatized at compile time. This is, just by reading the code, even before it is run! :)In order to do so there is only one condition: We MUST follow the rules, otherwise the compiler would not be able to automate the process at compile time. So, to ensure that we never break the rules, we are not allowed to explicitly write
release
,retain
, etc. Those calls are Automatically injected into our code by the compiler. Hence internally we still haveautorelease
s,retain
,release
, etc. It is just we don't need to write them anymore.The A of ARC is automatic at compile time, which is much better than at run time like garbage collection.
We still have
@autoreleasepool{...}
because having it does not break any of the rules, we are free create/drain our pool anytime we need it :).从方法返回新创建的对象需要自动释放池。例如,考虑这段代码:
在方法中创建的字符串的保留计数将为 1。现在谁来平衡保留计数和释放计数?
方法本身?不可能,它必须返回创建的对象,因此在返回之前不能释放它。
该方法的调用者?调用者并不期望检索一个需要释放的对象,方法名称并不暗示创建一个新对象,它只是表示返回一个对象,并且这个返回的对象可能是一个需要释放的新对象,但也可能是我们可能是现有的一个,但没有。该方法返回的内容甚至可能取决于某些内部状态,因此调用者无法知道是否必须释放该对象,并且不必关心。
如果调用者必须始终按照约定释放所有返回的对象,则每个不是新创建的对象在从方法返回之前都必须保留,并且一旦超出范围,调用者就必须将其释放,除非它再次返回。在许多情况下,这将是非常低效的,因为如果调用者并不总是释放返回的对象,则在许多情况下可以完全避免更改保留计数。
这就是为什么有自动释放池,所以第一个方法实际上会变成
在一个对象上调用
autorelease
将其添加到自动释放池中,但这到底是什么意思,将对象添加到自动释放池中?好吧,这意味着告诉你的系统“我希望你为我释放该对象,但在以后的某个时间,而不是现在;它有一个保留计数,需要通过释放来平衡,否则内存会泄漏,但我不能现在我自己做这件事,因为我需要该对象在我当前的范围之外保持活动状态,而我的调用者也不会为我做这件事,它不知道需要这样做,所以将其添加到您的池中,一旦您完成。清理那个池,也清理我的对象me."使用 ARC,编译器会为您决定何时保留对象、何时释放对象以及何时将其添加到自动释放池,但它仍然需要存在自动释放池才能返回新创建的对象从方法中获取对象而不泄漏内存。苹果刚刚对生成的代码做了一些巧妙的优化,有时会在运行时消除自动释放池。这些优化要求调用者和被调用者都使用 ARC(请记住,混合 ARC 和非 ARC 是合法的,并且也是官方支持的),并且是否确实如此只能在运行时知道。
考虑这个 ARC 代码:
系统生成的代码可以像下面的代码一样运行(这是允许您自由混合 ARC 和非 ARC 代码的安全版本):(
注意调用者中的保留/释放只是防御性安全保留,这不是严格要求的,没有它,代码将完全正确)
或者它可以像下面的代码一样运行,以防两者都在运行时检测到使用 ARC:
如您所见,Apple 消除了 atuorelease,因此还有池中的延迟对象释放被破坏,以及安全保留。要了解更多关于这是如何实现的以及幕后到底发生了什么,查看这篇博文。
现在讨论实际问题:为什么要使用
@autoreleasepool
?对于大多数开发人员来说,现在在代码中使用此构造的原因只有一个,那就是在适用的情况下保持较小的内存占用。例如,考虑这个循环:
假设每次调用
tempObjectForData
都可能创建一个新的TempObject
并返回 autorelease。 for 循环将创建一百万个临时对象,这些对象全部收集在当前的自动释放池中,并且只有在该池被销毁后,所有临时对象也会被销毁。在此之前,内存中将有一百万个临时对象。如果您编写这样的代码:
则每次 for 循环运行时都会创建一个新池,并在每次循环迭代结束时销毁。这样,尽管循环运行了一百万次,但任何时候最多有一个临时对象挂在内存中。
过去,在管理线程时(例如使用 NSThread),您通常还必须自己管理自动释放池,因为只有主线程会自动拥有 Cocoa/UIKit 应用程序的自动释放池。然而,这在今天几乎是遗留下来的,因为今天您可能不会一开始就使用线程。您可以使用 GCD DispatchQueue 或 NSOperationQueue ,这两个都可以为您管理顶级自动释放池,在运行块/任务之前创建并销毁一次完成了。
Autorelease pools are required for returning newly created objects from a method. E.g. consider this piece of code:
The string created in the method will have a retain count of one. Now who shall balance that retain count with a release?
The method itself? Not possible, it has to return the created object, so it must not release it prior to returning.
The caller of the method? The caller does not expect to retrieve an object that needs releasing, the method name does not imply that a new object is created, it only says that an object is returned and this returned object may be a new one requiring a release but it may as well be an existing one that doesn't. What the method does return may even depend on some internal state, so the the caller cannot know if it has to release that object and it shouldn't have to care.
If the caller had to always release all returned object by convention, then every object not newly created would always have to be retained prior to returning it from a method and it would have to be released by the caller once it goes out of scope, unless it is returned again. This would be highly inefficient in many cases as one can completely avoid altering retain counts in many cases if the caller will not always release the returned object.
That's why there are autorelease pools, so the first method will in fact become
Calling
autorelease
on an object adds it to the autorelease pool, but what does that really mean, adding an object to the autorelease pool? Well, it means telling your system "I want you to to release that object for me but at some later time, not now; it has a retain count that needs to be balanced by a release otherwise memory will leak but I cannot do that myself right now, as I need the object to stay alive beyond my current scope and my caller won't do it for me either, it has no knowledge that this needs to be done. So add it to your pool and once you clean up that pool, also clean up my object for me."With ARC the compiler decides for you when to retain an object, when to release an object and when to add it to an autorelease pool but it still requires the presence of autorelease pools to be able to return newly created objects from methods without leaking memory. Apple has just made some nifty optimizations to the generated code which will sometimes eliminate autorelease pools during runtime. These optimizations require that both, the caller and the callee are using ARC (remember mixing ARC and non-ARC is legal and also officially supported) and if that is actually the case can only be known at runtime.
Consider this ARC Code:
The code that the system generates, can either behave like the following code (that is the safe version that allows you to freely mix ARC and non-ARC code):
(Note the retain/release in the caller is just a defensive safety retain, it's not strictly required, the code would be perfectly correct without it)
Or it can behave like this code, in case that both are detected to use ARC at runtime:
As you can see, Apple eliminates the atuorelease, thus also the delayed object release when the pool is destroyed, as well as the safety retain. To learn more about how that is possible and what's really going on behind the scenes, check out this blog post.
Now to the actual question: Why would one use
@autoreleasepool
?For most developers, there's only one reason left today for using this construct in their code and that is to keep the memory footprint small where applicable. E.g. consider this loop:
Assume that every call to
tempObjectForData
may create a newTempObject
that is returned autorelease. The for-loop will create one million of these temp objects which are all collected in the current autoreleasepool and only once that pool is destroyed, all the temp objects are destroyed as well. Until that happens, you have one million of these temp objects in memory.If you write the code like this instead:
Then a new pool is created every time the for-loop runs and is destroyed at the end of each loop iteration. That way at most one temp object is hanging around in memory at any time despite the loop running one million times.
In the past you often had to also manage autoreleasepools yourself when managing threads (e.g. using
NSThread
) as only the main thread automatically has an autorelease pool for a Cocoa/UIKit app. Yet this is pretty much legacy today as today you probably wouldn't use threads to begin with. You'd use GCDDispatchQueue
's orNSOperationQueue
's and these two both do manage a top level autorelease pool for you, created before running a block/task and destroyed once done with it.这是因为您仍然需要向编译器提供有关自动释放对象何时超出范围是安全的提示。
It's because you still need to provide the compiler with hints about when it is safe for autoreleased objects to go out of scope.
引用自 https://developer.apple.com /library/mac/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmAutoreleasePools.html:
...
Quoted from https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmAutoreleasePools.html:
...
TL;DR
Objective-C 使用
@autoreleasepool
,Swift 使用autoreleasepool
来释放放入自动释放池的对象(通过使用autorelease
内)手动(不是在 RunLoop 迭代周期结束时)当您使用纯 Swift(没有 Objective-C 对象)并分配 Swift 对象时 - ARC 会自动处理它
但是如果您决定调用/使用
Foundation /旧版 Objective-C code
(NSData
,Data
...) 在内部使用autorelese
,然后在@autoreleasepool
中使用救援[MRC、ARC、GC]
TL;DR
@autoreleasepool
is used by Objective-C andautoreleasepool
by Swift torelease
objects which were put into auto release pool (by usingautorelease
inside) manually(not at the end of RunLoop iteration cycle)When you work with pure Swift(without Objective-C objects) and allocate Swift objects - ARC handles it automatically
But if you decide call/use
Foundation/Legacy Objective-C code
(NSData
,Data
...) which usesautorelese
inside then@autoreleasepool
in a rescue[MRC, ARC, GC]
这个主题似乎有很多困惑(至少有 80 个人现在可能对此感到困惑,并认为他们需要在代码中添加 @autoreleasepool)。
如果一个项目(包括其依赖项)专门使用 ARC,那么 @autoreleasepool 永远不需要使用,也不会做任何有用的事情。 ARC将在正确的时间处理释放对象。例如:
显示:
一旦值超出范围,每个测试对象就会被释放,而无需等待自动释放池退出。 (同样的情况也发生在 NSNumber 示例中;这只是让我们观察 dealloc。)ARC 不使用 autorelease。
仍然允许使用 @autoreleasepool 的原因是对于混合 ARC 和非 ARC 项目,这尚未完全过渡到 ARC。
如果调用非 ARC 代码,它可能会返回一个自动释放的对象。在这种情况下,上面的循环将会泄漏,因为当前的自动释放池永远不会退出。这就是您想要在代码块周围放置 @autoreleasepool 的地方。
但如果你已经完全完成了 ARC 过渡,那么就忘掉 autoreleasepool 吧。
There seems to be a lot of confusion on this topic (and at least 80 people who probably are now confused about this and think they need to sprinkle @autoreleasepool around their code).
If a project (including its dependencies) exclusively uses ARC, then @autoreleasepool never needs to be used and will do nothing useful. ARC will handle releasing objects at the correct time. For example:
displays:
Each Testing object is deallocated as soon as the value goes out of scope, without waiting for an autorelease pool to be exited. (The same thing happens with the NSNumber example; this just lets us observe the dealloc.) ARC does not use autorelease.
The reason @autoreleasepool is still allowed is for mixed ARC and non-ARC projects, which haven't yet completely transitioned to ARC.
If you call into non-ARC code, it may return an autoreleased object. In that case, the above loop would leak, since the current autorelease pool will never be exited. That's where you'd want to put an @autoreleasepool around the code block.
But if you've completely made the ARC transition, then forget about autoreleasepool.