博士TL;
为什么ARC仍然需要@autoreleasepool ?
@autoreleasepool被Objective-C和Swift用来处理内部的autorelese
当你使用纯Swift并分配Swift对象时- ARC处理它
但如果你决定调用/使用Foundation/Legacy Objective-C代码(NSData, Data),其中使用autorelese,则在救援中使用@autoreleasepool
//Swift
let imageData = try! Data(contentsOf: url)
//Data init uses Objective-C code with [NSData dataWithContentsOfURL] which uses `autorelese`
长回答
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
释放对象的正确方法:
如果只有这个-悬空指针。因为它仍然可以指向堆中的对象并且可以发送消息
= 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 You are able to own an object as many times as you need(you can call retain several times)
(-1) release If you an owner you must release it. If you release more than retainCount it will be 0
(-1) autorelease Adds an object, which should be released, to autorelease pool. This pool will be processed at the end of RunLoop iteration cycle(it means when all tasks will be finished on the stack)[About] and after that release will be applied for all objects in the pool
(-1) @autoreleasepool Forces process an autorelease pool at the end of block. It is used when you deal with autorelease in a loop and want to clear resources ASAP. If you don't do it your memory footprint will be constantly increasing
当你在方法调用中分配一个新对象并返回它时,Autorelease被使用
- (B *)foo {
B *b1 = [[B alloc] init]; //retainCount = 1
//fix - correct way - add it to fix wrong way
//[b1 autorelease];
//wrong way(without fix)
return b;
}
- (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最大的优点之一是它可以在编译时自动插入、保留、释放、自动释放,作为开发人员,你不需要再管它了
启用/禁用弧
//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
#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生成错误,您应该手动删除保留,释放,自动释放和其他问题
Edit -> Convert -> To Objective-C ARC...
新Xcode与MRC
如果你启用了MRC,你会得到下一个错误(警告)(但是构建会成功)
//release/retain/autorelease/retainCount
'release' is unavailable: not available in automatic reference counting mode
ARC forbids explicit message send of 'release'