TL;DR
为什么在ARC下还需要@autoreleasepool?
@autoreleasepool
被Objective-C和Swift用于处理内部的autorelese
当您使用纯Swift并分配Swift对象时,ARC会处理它
但是,如果您决定调用/使用Foundation / Legacy Objective-C code
(NSData
,Data
),该代码内部使用autorelease
,那么@autoreleasepool
就会派上用场。
let imageData = try! Data(contentsOf: url)
长答案
MRC, ARC, GC
手动引用计数(MRC)
或手动保留-释放(MRR)
,作为开发人员,您需要手动计算对象的引用计数。
自动引用计数(ARC)
在iOS v5.0和OS X Mountain Lion中引入,使用xCode v4.2。
垃圾收集(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时,对象将被系统解除分配。
- 分配对象
- 使用引用计数
- 解除分配对象。当
retainCount == 0
时调用deinit
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]
[a release]
a = nil
使用引用计数(对象所有者规则):
- (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 {
B *b1 = [[B alloc] init];
return b;
}
- (void)testFoo {
B *b2 = [a foo];
[b2 retain];
[b2 release];
}
@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是否已启用/禁用
使用预处理器
__has_feature
函数。
__has_feature(objc_arc)
编译时间
// error if ARC is Off. Force to enable ARC
//or
// error if ARC is On. Force to disable ARC
运行时
#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'