现在的位置: 首页 > 综合 > 正文

是否应该使用ARC

2013年05月17日 ⁄ 综合 ⁄ 共 2551字 ⁄ 字号 评论关闭

关于ARC的介绍文章网上已经很多,苹果的官方文档也不少。担心使用ARC会带来问题的同学主要的理由有以下5点:

  1. 担心这个技术方案不靠谱。苹果大多数时候的技术方案都是比较靠谱的,但也有一些技术方案有很多坑,例如storyboard。关于storyboard的问题可以参看我的这篇文章
  2. 原有的项目在非ARC环境下运行良好,担心迁移成本或引入新的问题。
  3. 苹果以前手工管理内存需要非常小心,稍微不注意应用程序就崩溃了。有过这段经历的iOS开发老手,心里上还是觉得自己手工管理内存更踏实一些。
  4. 使用ARC需要了解ARC的一些细节,还需要引入_bridge等新的关键字,学习成本还是有的。
  5. 以为ARC只能支持iOS5.0以上(这是非常大的误解)。

对于上面提到5点问题,我认为相应的回答如下:

  1. ARC是WWDC2011大会时提出的技术,离现在已经快2年了,而且苹果现在将MacOS上的垃圾回收机制废弃(Deprecated),采用ARC替代,无疑证明了ARC是成熟的了。
  2. 确实有一些迁移成本,但苹果在Xcode中专门集成了迁移工具,成本已经非常小了。如下图就是Xcode集成的将非ARC工程转换成ARC工程的工具。另外,为了兼容第三方的非ARC开源库,你也可以在工程中随意使用编译参数:-fno-objc-arc ,这个参数允许对部分文件关闭ARC。
  3. 手工管理内存虽然踏实,但是泄露很容易发生。常常开发完成后,需要使用Instruments来检测泄露。但用了ARC后,基本不会出现泄露了,我在开发粉笔网iPhone客户端时,由于使用了ARC,花三个月开发完的应用,用instruments检测后,没有发现任何内存泄漏问题。这在没有使用ARC的工程中是不可想象的。
  4. 确实有学习成本。但是非常值得学习,能省不少开发精力。
  5. 虽然ARC是与iOS5一同推出,但是由于ARC的实现机制是在编译期完成,所以使用ARC之后App仍然可以支持iOS4.3。稍微需要注意的是,如果要在ARC开启的情况下支持iOS4.3,需要将weak关键字换成 __unsafe_unretained,另外还有一些细节需要处理,在这里我就不展开说了。

所以,希望大家都能在项目中使用ARC,一旦你感受到它带来的好处,你就离不开它了。它也能让你从繁琐的内存管理代码中解放出来,将精力更多关注于代码结构、设计模式而不是底层的内存管理。

关于ARC的教程,除了苹果的官方文档外,推荐易飞杨写的ARC相关的文章(注:链接需要翻墙才能打开)。易飞杨的博客中关于iPhone开发的文章都写得很深入,值得好好阅读。

 

 __bridge简单记录:

我们先来看一下ARC无效的时候,我们写id类型转void*类型的写法:

id obj = [[NSObject alloc] init];
void *p = obj;
反过来,当把void*对象变回id类型时,只是简单地如下来写,

id obj = p;
[obj release];
但是上面的代码在ARC有效时,就有了下面的错误:

    error: implicit conversion of an Objective-C pointer
        to ’void *’ is disallowed with ARC
        void *p = obj;
                  ^
 
    error: implicit conversion of a non-Objective-C pointer
        type ’void *’ to ’id’ is disallowed with ARC
        id o = p;
                ^

__bridge
为了解决这一问题,我们使用 __bridge 关键字来实现id类型与void*类型的相互转换。看下面的例子。

id obj = [[NSObject alloc] init];
 
void *p = (__bridge void *)obj;
 
id o = (__bridge id)p;
将Objective-C的对象类型用 __bridge 转换为 void* 类型和使用 __unsafe_unretained 关键字修饰的变量是一样的。被代入对象的所有者需要明确对象生命周期的管理,不要出现异常访问的问题。

除过 __bridge 以外,还有两个 __bridge 相关的类型转换关键字:

__bridge_transfer
__bridge_retained
接下来,我们将看看这两个关键字的区别。

 

__bridge_retained
先来看使用 __bridge_retained 关键字的例子程序:

id obj = [[NSObject alloc] init];
 
void *p = (__bridge_retained void *)obj;
从名字上我们应该能理解其意义:类型被转换时,其对象的所有权也将被变换后变量所持有。如果不是ARC代码,类似下面的实现:

id obj = [[NSObject alloc] init];
 
void *p = obj;
[(id)p retain];
可以用一个实际的例子验证,对象所有权是否被持有。

void *p = 0;
 
{
    id obj = [[NSObject alloc] init];
    p = (__bridge_retained void *)obj;
}
 
NSLog(@"class=%@", [(__bridge id)p class]);
出了大括号的范围后,p 仍然指向一个有效的实体。说明他拥有该对象的所有权,该对象没有因为出其定义范围而被销毁。

__bridge_transfer
相反,当想把本来拥有对象所有权的变量,在类型转换后,让其释放原先所有权的时候,需要使用 __bridge_transfer 关键字。文字有点绕口,我们还是来看一段代码吧。

如果ARC无效的时候,我们可能需要写下面的代码。

// p 变量原先持有对象的所有权
id obj = (id)p;
[obj retain];
[(id)p release];
那么ARC有效后,我们可以用下面的代码来替换:

// p 变量原先持有对象的所有权
id obj = (__bridge_transfer id)p;
可以看出来,__bridge_retained 是编译器替我们做了 retain 操作,而 __bridge_transfer 是替我们做了 release1

抱歉!评论已关闭.