三年前,Xcode开发就一直推崇使用ARC内存管理机制, 再往前的岁月里使用的是MRC,那时候retain和release随处可见,显然MRC已经成为历史,着眼当下:
这里简要罗列了 ARC 的一些知识点概要,
这是一个学习并存在容错的文档翻译。
Transitioning to ARC Release Notes
1. 首先介绍一个概念:
现在的 OS X系统 和 iOS系统 都是没有 垃圾回收 的(GC Garbage Collection)苹果使用的内存管理的方法是: 应用计数 机制。从原有的MRC 过度到现在更加先进的ARC 机 制。
2. ARC 使用中感受到最大的好处(如果曾有MRC体验 will more feel):
对于果农来说,再也不用费尽心思考虑对象的retain 和 release 操作了,解放出庞大的能量,使得更能够专注在有趣的code环节
3. 概要一下ARC:
3.1 ARC和MRC都是对遵循着相同的内存管理法则,然而ARC更加优秀,它会在编译的时候(compile time)自动添加一些code来确保对象的必要和生命力。对于其可靠性,(maybe question apple)
3.2 为了确保编译器自动生成正确代码,ARC 对toll-free bridging(Coare Fundation <-> OC Object)方法做了限制约束;ARC 添加了一些新的对象引用的生命周期修饰符, 和一些属性声明。
3.3 ARC 起用于系统版本: Xcode4.2 , OS X v10.7 ,iOS 5 ,
3.4 Xcode 更改工程到ARC模式方法:(choose Edit > Convert to Objective-C ARC);另外对于当个文件,可以使用编译标识来启动或者取消ARC(Target > Build Phases > Compile Sources):弃用ARC 使用 -fobjc-arc ; 禁用ARC 使用 -fno-objc-arc标识对应文件即可。
4. ARC中一些硬性规则:
这些规则都是为了让内存管理模式更加可靠,这些规则只在ARC 编译模式下发生作用,既然是硬性的,如果违背就会即刻生成编译时报错。
4.1 不能实现或调用:retain,release,retainCount或者autorelease。拓展为不能用@selector(retain),@selector(relase)等。
也不能直接调用dealloc函数,dealloc 函数在ARC机制下不需要(也不能、会导致编译错误)调用【super dealloc】,它其实是在编译时候自动添加到代码中。对于Core Foundation-syle objects,任然可以使用CFRetain,CFRelease 。
4.2 不能使用 NSAllocateObject 或者 NSDeallocateObject。 使用alloc 穿件对象,运行时会处理好对象的释放。
4.3 不能在C的结构体重使用 对象指针,与其使用结构体,可以使用OC对象来管理数据。
4.4 id 和 void * 之间存在差异,id 其实是一个对象结构体指针, 在Objective-C对象和Core Foundation类型之间需要做一些特别定位处理
NSMutableArray *colors = [NSMutableArray arrayWithObject:(id)[[UIColor darkGrayColor] CGColor]];
[colors addObject:(id)[[UIColor lightGrayColor] CGColor]];
4.5 不能使用NSAutoreleasePool对象,ARC 提供了@autoreleasepool 块替代,比前者优秀很多。
4.6 无需再使用 NSZone ,OC的运行时机制会忽略它们
4.7 ARC 引入了一些对方法名和属性命名的约束。不能用 new 开头的给一个属性的访问器命名,除非设置了一个特意指定的getter:
// Won't work:
@property NSString *newTitle;
// Works:
@property (getter=theNewTitle) NSString *newTitle;
5. ARC 引入的生命时修饰符(Lifetime Qualifiers)
5.1 属性修饰(Property Attributes)
关键字 weak 和 strong。
下面的声明方法同义于: @property(retain) MyClass *myObject;//MRC
@property(strong) MyClass *myObject;//ARC
下面的声明方法相似于: @property(assign) MyClass *myObject;//MRC
@property(weak) MyClass *myObject
在ARC下, stong 是默认的对象属性类型。
5.2 变量修饰(Variable Qualifiers)
__strong 默认修饰,只要有一个强引用存在,对象就始终是‘alive’的。
__weak 特别标注不用保留引用对象的alive,当改对象不再存在强引用的时候,弱引用会将至置为nil。
__unsafe_unretained,不保留引用对象的alive,对象武强引用是也不置为nil,如果对象释放,指针将悬垂。
__autoreleasing,
使用方式(ClassName * qualifier variableName)如:
MyClass * __weak myWeakReference;
MyClass * __unsafe_unretained myUnsafeReference;
注意当 __weak 变量实在stack 上的时候:
NSString * __weak string = [[NSString alloc] initWithFormat:@"First Name: %@", [self firstName]];
NSLog(@"string: %@", string);
clang会给出警告:(Assigning retained object to weak variable;object will be released after assignment)
打印结果是:string: (null) ???不得解??
5.3 使用修饰符,避免强引用循环(Strong Reference Cycles)
Quote by Linus Torvalds:"Talk is cheap Show me the code":
As described, instead, you can use a __block qualifier and set the myController variable to nil in the completion handler:
MyViewController * __block myController = [[MyViewController alloc] init…];
// ...
myController.completionHandler = ^(NSInteger result) {
[myController dismissViewControllerAnimated:YES completion:nil];
myController = nil;
};
-----------------
Alternatively, you can use a temporary __weak variable. The following example illustrates a simple implementation:
MyViewController *myController = [[MyViewController alloc] init…];
// ...
MyViewController * __weak weakMyViewController = myController;
myController.completionHandler = ^(NSInteger result) {
[weakMyViewController dismissViewControllerAnimated:YES completion:nil];
};
--------------
For non-trivial cycles, however, you should use:
MyViewController *myController = [[MyViewController alloc] init…];
// ...
MyViewController * __weak weakMyController = myController;
myController.completionHandler = ^(NSInteger result) {
MyViewController *strongMyController = weakMyController;
if (strongMyController) {
// ...
[strongMyController dismissViewControllerAnimated:YES completion:nil];
// ...
}
else {
// Probably nothing...
}
};
6. ARC 使用心得语法去管理 AutoRelease Pools:
不能直接使用 类:NSAutoreleasePool,可以使用 @autoreleasepool 块语法,它更加强大。 如下
@autoreleasepool {
// Code, such as a loop that creates a large number of temporary objects.
}
这个简单的结构可以让编译器合理处理引用计数的状态。进入时,自动释放吃就被嵌入(pushed),当正常退出的时候(break,return,goto,fall-through,and so on )自动释放 池就被弹出,不过如果是意外退出的情况,释放池不会弹出(popped)。
7. outlets 应该使用weak, 但是nib文件中一些来至文件的持有者的高层级的对象需要使用strong (window,menu bars,custom controller objects, a storyboard scene)
8. Stack 变量将被初始化为nil。
- (void)myMethod {
NSString *name;
NSLog(@"name: %@", name);
}
9. 管理 Toll-Free Bridging
编译器不会自动管理Core Foundation 对象的内存。需要直接更具框架的内存管理规则 调用CFRetain 和 CFRelease。
如果需要在OC 和CF 之间做处理 利用宏定义(objc/runtime.h) 或者CF类型的宏(定义在NSObject.h)中。
__bridge CF 和 OC 之间指针转换 武转换持有关系。
__bridge_retained / CFBridgingRetain 将一个OC 指针 切换大奥CF指针,并且转移持有关系,需要自己负责释放持有对象关系(使用CFRelease)
__bridge_transfer / CFBridgingRelease 将非OC指针 切换为OC型,并且将持有关系交给ARC。
10.dealloc 函数
因为ARC 不会自动 malloc/free,不管理CF对象、文件描述符 等的生命周期,所以需要自己写一个dealloc函数来释放这些资源。
完全不用去对一个实例变量进行release,但是需要自己处理一些不遵循ARC的代码,如可能需要插入[self setDelegate:nil]到系统类中。
在ARC中是不允许对dealloc函数调用[super dealloc]的,应为在运行时会自动调用这块代码。
参考文档:ARC Release Notes