首页 > 其他 > 详细

深入剖析ARC

时间:2014-01-16 20:33:42      阅读:559      评论:0      收藏:0      [点我收藏+]

 

什么是ARC?
官方给出的解释是:Automatic Reference Counting (ARC) is a compiler feature that provides automatic memory management of Objective-C objects. Rather than having to think about retain and release operations, ARC allows you to concentrate on the interesting code, the object graphs, and the relationships between objects in your application.
自动引用计数是一种编译器特性,提供对象的自动内存管理。ARC使得你可以专注于你的应用中的代码,UI以及对象之间的关系而不需要去关心使用retain、release去进行繁冗的内存管理。

ARC通过增加编译时代码确保对象按需存在,按需释放。概念上来说,它与手动引用计数(MRC)遵从同样的规则:为你添加正确适当的内存管理代码。为了确保编译器生成正确的代码,ARC限制了你可以使用的方法和你使用toll-tree bridging的方式。Toll-free bridging,简称为TFB,是一种允许某些ObjC类与其对应的CoreFoundation类之间可以互换使用的机制。关于Toll-free bridging可以搜索官方文档,这里给出一篇推荐博文:清风徐来,水波不兴

ARC的支持:Xcode4.2以上,OS X v10.7以上,IOS5以上。可以参考官方原句:ARC is supported in Xcode 4.2 for OS X v10.6 and v10.7 (64-bit applications) and for iOS4 and iOS5.Weak references are not supported in OS X v10.6 and iOS 4.


ARC与MRC的区别及编程方式
MRC : Manual Reference Counting。使用retain来增加内存的引用计数,release来减少引用计数,autorelease来加入自动释放池等等。
ARC : Automatic Reference Counting。自动引用计数,绝不能再出现以上的三个关键字,实际上Xcode也会报错。

使用ARC需要遵循的新规则
  为了正确的工作,ARC规定了一些其他编译模式没有的新规则。这些规则旨在提供一个完全可信的内存管理模型,在一些情况下,他们仅执行最佳实践,而另一些情况下,他们简化你的代码或者明显的告知你不需要处理内存管理。如果你违反了这些规则,你会得到一个明显的编译时错误,而不是一个微小的错误但是在运行时却有可能成为一个大问题,问题可以及早的暴漏出来,而不是在程序有bug的时候再去苦苦的找bug,所以说ARC简直是程序员的福音。相比之下,还是遵守下面的这些规则比较简单:

1 . 不能显式调用dealloc,retain,release,retainCount,autorelease,或者是使用@selector(retain),@selector(release)等等。但是你可以如果你需要释放一些资源而不是释放实例变量时,你可以实现dealloc,在dealloc函数里面去完成这些工作,但是记住,不要去调用,因为编译器会帮你做的。同时,dealloc函数里也不要去调用super dealloa,这也是编译器的工作。但是你可能需要在系统类和其他未使用ARC的代码中调用[systemClassInstance setDelegate:nil] ,同时你也需要使用CFRetain,CFRelease和其他CoreFoundation对象的相关方法。理解了Toll-free bridging你或许会清楚这个问题。

2 . 不能使用NSAllocateObject或者NSDeallocateObject。使用alloc创建对象,系统在运行时会关注对象的生存周期并在适当的时候释放他。

3 . 不能在C结构体中使用对象指针。如果你要使用结构体,还不如直接建立一个Objective-C对象来管理数据。

4 . id与void *之间不能随意的转换。你必须使用特定的类型转换告诉编译器对象的生命周期,在Objective-C对象与作为函数传入参数的CoreFoundation对象之间你需要做这样的转换。

5 . 不能使用NSAutoreleasePool自动释放池。ARC提供@autoreleasepool代替,并且比NSAutoreleasePool更高效。

6 . 不能使用内存区(memmory zons)。再也没有必要使用NSZone,因为现代的Objective-C运行时会忽略它。

7 . 不能以new为开头去命名一个访问器,这意味着你不能声明一个以new开头的property,除非你附给他一个不同名字的getter:

@property NSString *newTitle;                       //错误的
@property (getter=theNewTitle) NSString *newTitle;         //正确的

ARC的编程方式
  ARC引入了新的对象生命周期修饰符和弱引用。weak reference弱引用不会增加它所指向的对象的引用计数,而且当它所指向的对象引用计数变为0时会自动的变成nil,这样我们不用担心会指向已经释放的内存,EXC_BAD_ACCESS错误也会减少很多。你也可以充分使用这些修饰符来管理你的程序中的对象,比如说,ARC无法避免强循环引用,但是你可以巧妙的使用weak修饰符来确保你的程序不会有循环引用。

weakstrong是新引入的属性修饰符,例子如下:

1 @property(strong) MyClass *myObject;       //强引用,同retain
2 @property(weak)   MyClass *myObject;       //弱引用,同assign,当所指向的对象被释放后被自动的置为nil

在ARC的工程中,strong是对象的默认属性,但是为了避免歧义最好还是显式的指明。

变量修饰符
上面介绍了属性修饰符,下面来看看这几款变量修饰符。const你一定使用过,类似的变量修饰符还有这些:

__strong
__weak
__unsafe_unretained
__autoreleasing

__strong:缺省的修饰符,当对象有strong指针指向他时,他是不被释放的,alive。
__weak:保留对对象的弱引用指针,不增加引用计数,因此也对对象的生命周期没有保障,当所指向的对象被释放后自动置为nil。
__unsafe_unretained:保留对象的弱引用指针,当所指向的对象被释放后,该指针不会被置为nil,变成野指针。
__autoreleasing:修饰以引用(id *)传入的参数,并且当return后会被自动释放。主要用于引用传入参数。

变量修饰符的使用格式:

ClassName * qualifier variableName;

例如:

MyClass * __weak myWeakReference;
MyClass * __unsafe_unretained myUnsafeReference;

虽说编译器很伟大但偶尔也会小粗心一下,例如下面的错误就不会被报错,只是小警告,所以不要忽略警告哦~下面的例子是错误示例:

NSString * __weak string = [[NSString alloc] initWithFormat:@"First Name: %@", [self firstName]];
NSLog(@"string: %@", string);

 虽然string在刚创建并初始化完成后使用,但是并没有一个强指针指向该对象,因此他一初始化完成就由于引用计数为0被释放了。结果输出的是null,原因你懂的,上面说过三遍。

通过引用传入的对象也需要注意,下面的例子是正确的:

-(BOOL)performOperationWithError:(NSError * __autoreleasing *)error;
bubuko.com,布布扣
NSError *error;
BOOL OK = [myObject performOperationWithError:&error];
if (!OK) {
    //use
}
bubuko.com,布布扣

 编译器会将代码补全为如下:

bubuko.com,布布扣
1 NSError * __strong error;
2 NSError * __autoreleasing tmp = error;
3 BOOL OK = [myObject performOperationWithError:&tmp];
4 error = tmp;
5 if (!OK) {
6     //use
7 }
bubuko.com,布布扣

  以__autoreleasing声明参数其实是告诉ARC编译器,这个函数没有这个对象的拥有权,所以在函数里面的read和write该引用所指向的对象时,就将它放到自动释放池吧,return后就释放掉,当然这些是编译器做的事。官方文档告诉我们:传入的本地变量为__strong(默认的)与形参__autoreleasing不匹配导致了编译器会生成一个临时变量tmp,你可以像以上这样做,声明参数为__autoreleasing,同时也可以通过将参数声明为id __strong *来获得__strong变量的地址。(现在尚没有想清楚这两种有什么区别


 ARC中如何避免循环引用?

  你可以使用对象的生命周期修饰符来避免强引用循环而导致的互相引用内存不能被释放,从而造成内存泄露。例如,你可以设计一个父子对象图,父对象对子对象可以强引用strong,同时子对象对父对象的引用为弱类型的引用weak。

  巧妙的使用weak弱引用是避免循环引用的良药,这就是通常为什么delegate为弱类型的原因。另外最容易循环引用的一块就是block内对自身的属性引用,这又是另外一块很重要的问题,将在后续的博客中单独作为一篇来讲解。


 如何将设置工程为ARC?

1.新建ARC工程
 新的编译器新建的工程默认支持ARC,查看Objective-C Automatic Reference Counting选项,YES表示支持ARC。

2.将旧MRC工程转化为ARC
 (1)首先查看编译器是否支持,如果不支持选择编译器或者升级编译器,记住ARC是LLVM 3.0及以后的新特性,之前的编译器不支持,如果你的Compiler选项还在之前,那赶紧升级Xcode吧,否则新时代已经不适合你咯~按照下面的图示选择即可,我的Xcode是5.0的,编译器也是5.0。
  bubuko.com,布布扣

 (2)同样是在Build Settings里面,将Objective-C Automatic Reference Counting选项改为YES,如下:
     bubuko.com,布布扣

 (3)run一下工程吧,很多错误吧,一个一个改正吧~按照我们前面提到的ARC的规则,将一些retain,release等相关MRC字段都改为ARC规则的,将一些property属性,retain改为strong,assign改为weak,不能显示调用dealloc函数等等,记住一些基础类型如BOOL等还是assign。对于一个小工程来说手动修改很容易,但是工程很大就比较费脑筋了,幸亏伟大的Xcode提供了自动转换到ARC的功能。具体就不再细说,可以参考这个牛人的博客里面的例子:人淡如菊,讲的已经非常详细。同时也会有些非ARC的第三方库那就直接将库设置编译属性为非ARC吧,不要去修改库了。另外,Core Foundation的一些东西也会牵扯到桥接的内容,后续博客再详细介绍。

3.设置ARC工程中兼容MRC的文件
 在工程的Build Phases中的Compile Sources选项中找到对应的.m文件,将其Compiler Flags设置为-fobjc-arc,这样单个文件就支持ARC了。而如果ARC的工程中里,想要设置某个单个文件不支持ARC,同样的操作步骤,将其Compiler Flags设置为-fno-objc-arc


 一些推荐阅读的内容:

官方ARC文档(英文的,本篇博客大部分都翻译于此,另外也有些ARC相关的重要内容,后续我会继续翻译) 

OneV‘s Den的这篇文章(很详细,耐着性子看完会很有收获)

Clang 3.0 Document (超长哦,是我翻译的目标)


 

声明:本博客全部自己手动整理,翻译了官方博客,并且用自己的话整理了一些内容,如有问题,请在下面回复,谢谢!

深入剖析ARC

原文:http://www.cnblogs.com/xinguan/p/3518472.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!