综述:
在项目中使用 ARC之后,所有的编程都和以前一样,除了你不再调用 retain, release, autorelease。启用 ARC 之后,编译器会自动在适当的地方插入retain, release, autorelease 语句。
ARC 的规则:
只要还有一个变量指向对象,对象就会保存在内存中。
ARC 的限制:
ARC 只能工作于 Objective-C 对象,如果 应用使用了 Core Foundation 或 malloc()/free(),此时需要你来管理内 存。
“strong”指针和“weak”指针:
“strong”指针:能够保持对象的生命。
因为 strong 指针会保持对象的生命,某些情况下你需要手动设置这些指针为 nil,否则可能导致应用内存不足。
(默认所有实例变量和本地变量都是 strong 类型的指针,一般strong 变量不加 __strong 修饰。)
“weak”指针:weak 变量仍然指向一个对象,但不是对象的拥有者,“weak”指针所指内容被释放时,“weak”指针会被自动置为nil.
典型的例子是 delegate 模式:
(你的ViewController通过strong指针拥有一个UITableView, TableView 的 data source 和 delegate 都是weak指针,指向ViewController)。
对象之间的关联:
启用ARC之后,不再需要考虑什么时候 retain 或 release 对象。唯一需要考虑的是对象之间的关联,谁拥有该对象,以及这个对象需要存活多久。
一个例子:
以下代码在 ARC 之前是不可能的,在手动内存管理中,从 Array 中 移除一个对象会使对象不可用,对象不属于Array 时会立即被释放。随后 NSLog()打印该对象就会导致应用崩溃。
id obj =[array objectAtIndex:0];
[array removeObjectAtIndex:0];
NSLog(@"%@", obj);
在ARC 中,这段代码是完全合法的,因为 obj 变量是一个 strong 指针, 它成为了对象的拥有者,从 Array 中移除该对象也不会导致对象被释放。
ARC 的迁移:
要启用一个项目的 ARC,你有以下几种选择:
1. Xcode 带了一个自动转换工具,可以迁移源代码至 ARC
2. 你可以手动转换源文件
3. 你可以在 Xcode 中禁用某些文件使用 ARC,这点对于第三方库非常有用。
Xcode 的自动迁移工具:
1, ARC 是 LLVM3.0编译器的特性,而现有工程可能使用老的 GCC4.2 或 LLVM-GCC编译器,因此首先需要设置使LLVM 3.0 编译器。
Project Settings -> target -> Build Settings,在搜索框中输入 compiler, 就可以列出编译器选项设置:
另外最好也选上 Warnings 中的 Other Warning Flags 为 -Wall,这样编译 器就会检查所有可能的警告,有助于我们避免潜在的问题。
同样,Build Options 下面的 Run Static Analyzer 选项也最好启用,这样 每次 Xcode 编译项目时,都会运行静态代码分析工具来检查我们的代码。
2, Build Settings 下面,选择“All”,搜索框输入"automatic",可以设置 "Objective-C Automatic Reference Counting"选项为 Yes,不过 Xcode 自动转 换工具会自动设置这个选项。
3, Xcode 的 ARC 自动转换工具: Edit->Refactor->Convert to Objective-C ARC
4, 选择需要转换的文件,Xcode 可能会提示项目不能转换为 ARC。此时:
打开 Xcode Preferences 窗口,选择 General 标签,启用 Continue building after errors 选项,再次执行 Edit\Refactor\Convert to Objective-C ARC:
5, 再有错误的情况下,就需要手工修改某些源代码。
注意:Xcode 的自动转换工具最好只使用一次。多次使用可能会出现比较诡异的问题。假如你第一次转换没有转换所有的文件,当你稍后试图再次转换剩余的 文件时,Xcode 实际上不会执行任何转换操作。因此最好一次就完成转换,没有 转换的文件可以考虑手工进行修改。
禁止某些文件的ARC:
最简单的方法是直接使用 Xcode 的 ARC 转换工具,取消选中那些不希望 进行ARC转换的源文件,这样Xcode会自动对这些文件设置 -fno-objc-arc标 志。
手动修改:对于某些我们不希望使用 ARC 的文件,例如第三方库源文件,可以在 Project Settings -> Build Phases 中,对这些文件选中 -fno-objc-arc 标志。
另外,可以使用预处理指令在必要时保持与 ARC 兼容:
#if _has_feature(objc_arc)
//do your ARC thing here
#endif
或者假如你还想支持老的 GCC compiler:
#if defined(_has_feature )&& _has_feature(objc_arc)
//do your ARC thing here
#endif
ARC 自动迁移的常见问题:
1,"Switch case is in protected scope"
switch (x)
{
case Y:
NSString *s =…;
Break;
}
ARC 不允许这样的代码,指针变量需要定义在 switch 语句外面,或者使用{}定义一个新的块:
switch (x)
{
case Y:
{
NSString *s =…;
Break;
}
}
这样 ARC 才能确定变量的作用域,从而在适当的时候释放该变量。
2,"ARC forbids Objective-C objects in structs or unions"
使用 ARC 之后,C Struct 中不能使用 Objective-C 对象, 解决办法是定义一个 Objective-C 类,不使用 C Struct。
Property总结:
- strong:等同于"retain",属性成为对象的拥有者
- weak:属性是 weak pointer,当对象释放时会自动设置为 nil,记住 Outlet 应该使用 Weak
- unsafe_unretained:等同于之前的"assign",只有 iOS 4 才应该使用。
(unsafe_unretained 指针和 weak 指针不一样的是,当相关联的对象释放时, 指针不会被设置为 nil,因此它实际上指向不存在的对象。)
dealloc 方法:
启用 ARC 之后,dealloc 方法在大部分时候都不再需要了。如果你的 dealloc 方法处理了其它资源(非内存)的释放,如定时器、Core Foundation 对象,则你仍然需要在 dealloc 方法中进行手动释放,如 CFRelease(), free()等。
Toll-Free Bridging:
当你在 Objective-C 和 Core Foundation 对象之间进行转换时,就需要使用 Bridge cast。(比如CFStringRef与NSString)
- 使用 CFBridgingRelease(),从 Core Foundation 传递所有权给 Objective-C;
- 使用 CFBridgingRetain(),从 Objective-C 传递所有权给 Core Foundation;
- 使用__brideg,表示临时使用某种类型,不改变对象的所有权。
iOS 4 中使用 ARC:
ARC 主要是 LLVM 3.0 编译器(而不是 iOS 5)的新特性,因此你也可以在 iOS 4.0之后的系统中使用ARC,不过需要注意的是,weak 指针需要iOS5才能使用。 如果你要在 iOS 4 中部署 ARC 应用,你就不能使用 weak property 和__weak 变 量。
ARC 高级指南:
1,Blocks 与 ARC。(指南里面用了很大的篇幅讲解)
2,Singleton 与 ARC。
3,Cocos2D 和 Box2D。
4,Autorelease 和 AutoreleasePool。
ARC 仍然保留了 AutoreleasePool,但是采用了新的 Block 语法:@autoreleasepool.
在 ARC 中,方法名如果以 alloc, init, new, copy, mutableCopy 开头,就是返回 retain 的对象,其它方法全部返回 autorelease 的对象。这条规则实际 上与手动内存管理是一样的。
autorelease 对象则是在 autorelease pool 排干( drain ) 时才会被释放。在 ARC 之前,你需要调用 NSAutoreleasePool 对象的 [drain] 或 [release] 方法,现在则是直接在 @autoreleasepool 块退出时进行 drain:
@autoreleasepool
{
NSString *s = [NSString stringWithFormat:…];
}// the string object is deallocated here
但是如果你像下面这样编写代码,则即使 NSString 对象在@autoreleasepool 块中创建,stringWithFormat:方法也确实返回了一个 autorelease 对象,但变 量 s 是 strong 类型的,只要 s 没有退出作用域,string 对象就会一直存在:
NSString *s;
@autoreleasepool
{
s = [NSString stringWithFormat:…];
}// the string object is still alive here
使用 __autoreleasing 可以使 autorelease pool 释放掉该对象,它告诉编 译器这个变量指向的对象可以被 autorelease,此时变量不是 strong 指针, string 对象会在@autoreleasepool 块的末尾被释放。不过注意变量在对象释放后,仍然会继续指向一个死掉的对象。如果你继续使用它,应用就会崩溃。代码如下:
_autoreleasing NSString *s;
@autoreleasepool
{
s = [NSString stringWithFormat:…];
}// the string object is deallocated here
NSLog(@”%@”,s);//crash!
注意 :Core Foundation 对象不能 autorelease , autorelease 完全纯属于 Objective-C。