在大多数情况下使用ARC(自动引用计数),我们不需要考虑使用Objective-C对象的内存管理。不允许再创建NSAutoreleasePool
,但是有一种新的语法:
@autoreleasepool {
…
}
我的问题是,当我不应该手动释放/自动释放时,为什么我会需要这个呢?
编辑:总结一下我从所有答案和评论中得到的结论:
新语法:
@autoreleasepool { … }
是
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
…
[pool drain];
更重要的是:
autorelease
以及release
。@autoreleasepool
:
myRunLoop(…) { @autoreleasepool { … } return success; }
。答案 0 :(得分:208)
ARC没有摆脱保留,发布和自动释放,它只是为您添加所需的。所以仍有调用保留,仍有调用释放,仍有调用自动释放,但仍有自动释放池。
他们使用新的Clang 3.0编译器和ARC进行的其他一项更改是,他们用NSAutoReleasePool
编译器指令替换了@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
循环中没有for
,那么你将在以后释放100000000个对象,而不是每次释放10000个对象。外for
- 循环。
<强>更新强>
另请参阅此答案 - https://stackoverflow.com/a/7950636/1068248 - 为什么@autoreleasepool
与ARC无关。
<强>更新强>
我看了一下这里发生了什么的内部结构和wrote it up on my blog。如果您查看那里,那么您将看到ARC正在做什么以及新样式@autoreleasepool
以及它如何引入范围如何被编译器用于推断有关保留,发布和保留的信息。自动释放是必需的。
答案 1 :(得分:14)
@autoreleasepool
不会自动释放任何内容。它创建一个自动释放池,以便在到达块结束时,在块处于活动状态时由ARC自动释放的任何对象将被发送释放消息。 Apple的Advanced Memory Management Programming Guide解释了这一点:
在自动释放池块的末尾,在块中接收到自动释放消息的对象被发送一个释放消息 - 一个对象在每次在块中发送自动释放消息时都会收到释放消息。
答案 2 :(得分:7)
人们常常误解ARC某种垃圾收集等。事实是,过了一段时间,Apple的人们(感谢llvm和clang项目)意识到Objective-C的内存管理(所有retains
和releases
等)可以完全自动化< em>编译时间。这就是通过阅读代码,甚至在它运行之前! :)
为了做到这一点,只有一个条件:我们必须遵循rules,否则编译器将无法在编译时自动化该过程。因此,为了确保我们永远不会违反规则,我们不允许显式编写release
,retain
等。这些调用由编译器自动注入我们的代码中。因此,在内部我们仍然有autorelease
s,retain
,release
等。我们不需要再写它们了。
ARC的A在编译时是自动的,这比在垃圾收集时的运行时要好得多。
我们仍然有@autoreleasepool{...}
因为它没有违反任何规则,我们可以随时创建/排空我们的游泳池:)。
答案 3 :(得分:3)
这是因为您仍然需要为编译器提供有关何时安全自动释放对象超出范围的提示。
答案 4 :(得分:1)
自动释放池块和线程
Cocoa应用程序中的每个线程都维护着自己的堆栈 自动释放池块。如果您正在编写仅限基金会的计划 或者如果你分离一个线程,你需要创建自己的自动释放 泳池区。
如果您的应用程序或线程长寿并可能生成 很多自动释放的对象,你应该使用自动释放池块 (就像AppKit和UIKit在主线程上做的那样);否则,自动释放 对象累积,内存占用增长。如果你的超然 线程不会使Cocoa调用,您不需要使用 自动释放池块。
注意:如果使用POSIX线程API创建辅助线程 而不是NSThread,除非Cocoa在,否则你不能使用Cocoa 多线程模式。 Cocoa之后才进入多线程模式 分离其第一个NSThread对象。在辅助POSIX上使用Cocoa 线程,您的应用程序必须首先分离至少一个NSThread 对象,可以立即退出。你可以测试Cocoa是否在 使用NSThread类方法的多线程模式是MultiThreaded。
...
在自动参考计数或ARC中,系统使用相同的参考计数 引用计数系统作为MRR,但它插入了适当的内存 管理方法在编译时调用。你是强烈的 鼓励将ARC用于新项目。如果您使用ARC,则有 通常无需了解底层实现 虽然在某些情况下可能会在本文档中描述 很有帮助。有关ARC的更多信息,请参阅转换为ARC发行说明。
答案 5 :(得分:0)
从方法返回新创建的对象时需要自动释放池。例如。考虑这段代码:
- (NSString *)messageOfTheDay {
return [[NSString alloc] initWithFormat:@"Hello %@!", self.username];
}
在方法中创建的字符串的保留计数为1。现在谁来平衡保留人数和释放人数呢?
方法本身?不可能,它必须返回创建的对象,因此它一定不能在返回之前释放它。
方法的调用者?调用者不希望检索需要释放的对象,方法名称并不意味着要创建一个新对象,它仅表示已返回一个对象,并且此返回的对象可能是需要释放的新对象,但它可能会成为一个现有的没有的。该方法返回的结果甚至可能取决于某些内部状态,因此调用方无法知道它是否必须释放该对象,并且不必理会。 p>
如果调用者必须始终按照约定释放所有返回的对象,则必须重新保留每个新创建的对象,然后再从方法中将其返回,并且一旦退出该对象,调用者必须将其释放作用域,除非再次返回。在许多情况下,这将是非常低效的,因为在很多情况下,如果调用者不总是释放返回的对象,则可以完全避免更改保留计数。
这就是为什么有自动释放池的原因,因此第一种方法实际上将变为
- (NSString *)messageOfTheDay {
NSString * res = [[NSString alloc] initWithFormat:@"Hello %@!", self.username];
return [res autorelease];
}
在对象上调用autorelease
会将其添加到自动释放池中,但是将对象添加到自动释放池中到底意味着什么?好吧,这意味着告诉您的系统“ 我要您为我释放该对象,但是稍后,而不是现在;它的保留计数需要通过释放来平衡,否则内存会泄漏但我无法现在我自己做,因为我需要使对象保持活动状态超出我当前的范围,而调用者也不会为我这样做,它也不知道需要这样做,因此将其添加到池中,清理那个游泳池,也帮我清理我的物品。“
借助ARC,编译器会为您决定何时保留对象,何时释放对象以及何时将其添加到自动释放池中,但是仍然需要自动释放池才能从方法中返回新创建的对象而无需内存泄漏。 Apple刚刚对生成的代码进行了一些漂亮的优化,这些优化有时会在运行时消除自动释放池。这些优化要求调用方和被调用方都使用ARC(请记住,混合使用ARC和非ARC是合法的,并且也得到正式支持),并且如果确实如此,则只能在运行时知道。
考虑此ARC代码:
// Callee
- (SomeObject *)getSomeObject {
return [[SomeObject alloc] init];
}
// Caller
SomeObject * obj = [self getSomeObject];
[obj doStuff];
系统生成的代码可以像以下代码一样运行(这是安全的版本,可让您自由组合ARC和非ARC代码):
// Callee
- (SomeObject *)getSomeObject {
return [[[SomeObject alloc] init] autorelease];
}
// Caller
SomeObject * obj = [[self getSomeObject] retain];
[obj doStuff];
[obj release];
(请注意,呼叫者中的保留/释放只是防御性的安全保留,并非严格要求,没有它,代码将是完全正确的)
或者,如果在运行时检测到两者都使用ARC,它的行为也可能类似于此代码:
// Callee
- (SomeObject *)getSomeObject {
return [[SomeObject alloc] init];
}
// Caller
SomeObject * obj = [self getSomeObject];
[obj doStuff];
[obj release];
如您所见,Apple消除了atuorelease,从而消除了销毁池时延迟释放的对象以及安全性。要了解有关如何实现以及幕后实际情况的更多信息,check out this blog post.
现在要提一个实际的问题:为什么一个人会使用@autoreleasepool
?
对于大多数开发人员而言,今天在他们的代码中使用此构造仅剩下一个原因,那就是在适用的情况下将内存占用保持在较小的水平。例如。考虑一下这个循环:
for (int i = 0; i < 1000000; i++) {
// ... code ...
TempObject * to = [TempObject tempObjectForData:...];
// ... do something with to ...
}
假设每次对tempObjectForData
的调用都可以创建一个新的TempObject
,该返回的值将自动释放。 for循环将创建这些临时对象中的一百万个,这些临时对象全部收集在当前的autoreleasepool中,并且只有在销毁该池后,所有临时对象也会被销毁。在此之前,您的内存中只有一百万个临时对象。
如果您改写这样的代码:
for (int i = 0; i < 1000000; i++) @autoreleasepool {
// ... code ...
TempObject * to = [TempObject tempObjectForData:...];
// ... do something with to ...
}
然后,每次for循环运行时都会创建一个新池,并在每次循环迭代结束时将其销毁。这样一来,尽管循环运行了100万次,但随时最多有一个临时对象在内存中徘徊。
过去,在管理线程时(例如,使用NSThread
),您通常还必须自己管理自动释放池,因为只有主线程自动具有Cocoa / UIKit应用程序的自动释放池。但是,今天这几乎已经成为历史,因为今天您可能不会开始使用线程。您将使用DispatchQueue
或NSOperationQueue
的GCD,这两个都可以为您管理一个顶级自动释放池,该池是在运行块/任务之前创建的,并在完成后销毁。 >
答案 6 :(得分:0)
TL;DR
<块引用>为什么 ARC 还需要@autoreleasepool?
@autoreleasepool
被 Objective-C 和 Swift 用于处理使用 autorelese
的 MRC Objective-C 代码,例如 NSData
、Data
长答案
MRC、ARC、GC
Manual Reference Counting(MRC)
或 Manual Retain-Release(MRR)
作为开发人员,您有责任手动计算对象上的引用
Automatic Reference Counting(ARC)
是在 iOS v5.0 和 OS X Mountain Lion 和 xCode v4.2 中引入的
Garbage Collection(GC)
可用于 Mac OS,但在 OS X Mountain Lion 中已弃用。必须转移到 ARC
MRC 和 ARC 中的引用计数
//MRC
NSLog(@"Retain Count: %d", [variable retainCount]);
//ARC
NSLog(@"Retain Count: %ld", CFGetRetainCount((__bridge CFTypeRef) variable));
堆中的每个对象都有一个整数值,表示在它上面指出了多少引用。当它等于 0 对象被释放系统
deinit
在 retainCount == 0
MRC
A *a1 = [[A alloc] init]; //this A object retainCount = 1
A *a2 = a1;
[a2 retain]; //this A object retainCount = 2
// a1, a2 -> object in heap with retainCount
释放对象的正确方法:
release
如果只有这个 - 悬空指针。因为它仍然可以指向堆中的对象并且可以发送消息 = nil
如果只是这样 - 内存泄漏。 deinit 不会被调用A *a = [[A alloc] init]; //++retainCount = 1
[a release]; //--retainCount = 0
a = nil; //guarantees that even somebody else has a reference to the object, and we try to send some message thought variable `a` this message will be just skipped
使用引用计数(对象所有者规则):
-(0 -> 1) alloc
、new
、copy
、mutableCopy
-(+1) retain
您可以根据需要多次拥有一个对象(您可以多次调用 retain
)
-(-1) release
。如果您是所有者,则必须将其释放。如果您释放的数量超过 retainCount,它将为 0
-(-1) autorelease
。添加一个应该被释放到 autorelease pool
的对象。该池将在RunLoop 迭代周期结束(这意味着当所有任务都在堆栈上完成时)进行处理,之后release
将应用于池中的所有对象
-(-1) @autoreleasepool
。强制在块结束处理自动释放池。当您在循环中处理 autorelease
并希望尽快清除资源时使用它。如果你不这样做,你的内存占用会不断增加
autorelease
用于在方法调用中分配新对象并返回它
- (B *)foo {
// NSString *label = [[NSString alloc] initWithString:@"Hello World"];
B *b1 = [[B alloc] init]; //retainCount = 1
//wrong way to run testFoo()
return b;
//correct way
//[b1 autorelease];
}
- (void)testFoo {
B *b2 = [a foo];
[b2 retain]; //retainCount = 2
//some logic
[b2 release]; //retainCount = 1
//Memory Leak
}
@autoreleasepool
- (void)testFoo {
for(i=0; i<100; i++) {
B *b2 = [a foo];
//process b2
}
}
ARC
ARC
的最大优点之一是它会在 编译时间 中自动插入 retain
、release
、autorelease
并作为开发人员你不应该再照顾它了
启用/禁用 ARC
//enable
-fobjc-arc
//disable
-fno-objc-arc
优先级从高到低的变体
//1. local file - most priority
Build Phases -> Compile Sources -> Compiler Flags(Select files -> Enter)
//2. global
Build Settings -> Other C Flags(OTHER_CFLAGS)
//3. global
Build Settings -> Objective-C Automatic Reference Counting(CLANG_ENABLE_OBJC_ARC)
检查 ARC 是否启用/禁用
Preprocessor
__has_feature
函数被使用
__has_feature(objc_arc)
编译时间
// error if ARC is Off. Force to enable ARC
#if ! __has_feature(objc_arc)
#error Please enable ARC for this file
#endif
//or
// error if ARC is On. Force to disable ARC
#if __has_feature(objc_arc)
#error Please disable ARC for this file
#endif
运行时
#if __has_feature(objc_arc)
// ARC is On
NSLog(@"ARC on");
#else
// ARC is Off
NSLog(@"ARC off");
#endif
逆向工程(针对 Objective-C)
//ARC is enabled
otool -I -v <binary_path> | grep "<mrc_message>"
//e.g.
otool -I -v "/Users/alex/ARC_experiments.app/ARC_experiments" | grep "_objc_release"
//result
0x00000001000080e0 748 _objc_release
//<mrc_message>
_objc_retain
_objc_release
_objc_autoreleaseReturnValue
_objc_retainAutoreleaseReturnValue
_objc_retainAutoreleasedReturnValue
_objc_storeStrong
将 Objective-C MRC 迁移到 ARC 的工具
ARC 生成错误,您应该手动删除 retain
、release
、autorelease
和其他问题
Edit -> Convert -> To Objective-C ARC...
带有 MRC 的新 Xcode
如果您启用 MRC,您会收到下一个错误(警告)(但构建会成功)
//release/retain/autorelease/retainCount
'release' is unavailable: not available in automatic reference counting mode
ARC forbids explicit message send of 'release'
答案 7 :(得分:-4)
这个话题似乎有很多混乱(至少有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对象都会被释放,而不会等待退出自动释放池。 (同样的事情发生在NSNumber示例中;这只是让我们观察dealloc。) ARC不使用自动释放。
仍允许使用@autoreleasepool的原因是针对混合ARC和非ARC项目,这些项目尚未完全转换为ARC。
如果您调用非ARC代码,它可能会返回一个自动释放的对象。在这种情况下,上面的循环会泄漏,因为永远不会退出当前的自动释放池。那就是你想在代码块周围加上@autoreleasepool的地方。
但是如果你完全进行了ARC转换,那么就忘掉autoreleasepool。