首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 移动开发 > Iphone >

iPhone开发之深入显出 (7) — ARC总结

2012-07-22 
iPhone开发之深入浅出 (7) — ARC总结?通过前面几篇文章的介绍,我想大家应该对ARC有了一个比较完整的理解。

iPhone开发之深入浅出 (7) — ARC总结

?

通过前面几篇文章的介绍,我想大家应该对ARC有了一个比较完整的理解。最后,我们来对ARC做一个总结,并把一些未涉及到的细节部分再深入讨论一下。

内存管理基本原则
内存管理的依循下面的基本原则
  • 自己生成的对象,那么既是其持有者
  • 不是自己生成的对象,也可成为其持有者(一个对象可以被多个人持有)
  • 如果不想持有对象的时候,必须释放其所有权
  • 不能释放已不再持有所有权的对象

    不管ARC有没有效,该原则始终存在。

    所有权关键字

    从代码上看,有ARC的代码和没有ARC的代码区别就在下面的几个关键字。

    类似 NSObject* 的对象类型,或者 id 类型1,当ARC有效的时候,根据具体情况,这些关键字必须要使用2。

    • __strong
    • __weak
    • __unsafe_unretained
    • __autoreleasing

      __strong是默认的修饰符。

      __weak修饰了一个自动nil的weak引用。

      __unsafe_unretained声明了一个不会自动nil的weak引用。当变量被释放,那么它就变成了一个野指针了。

      __autoreleasing 用来修饰一个声明为 (id *) 的函数的参数,当函数返回值时被释放。

      接下来,我们结合下面ARC的使用准则,来看看一些使用ARC后的技术细节。

      ARC使用准则

      为了比秒程序秒退的尴尬,ARC有效时,我们的代码必须遵循下面的准则。

      • 不能使用 retain/release/retainCount/autorelease
      • 不能使用 NSAllocateObject/NSDeallocateObject
      • 不能使用 NSZone
      • 不能明示调用dealloc
      • 内存管理相关的函数必须遵循命名规则
      • 使用@autoreleasepool代替NSAutoreleasePool
      • Objective-C 对象不能作为C语言结构体(struct/union)的成员
      • 【id】与【void*】之间需要明示cast

        建议使用Objective-C的class来管理数据格式,来代替C语言的struct。不能隐式转换 id 和 void *。

        让我们一个一个来分析

        不能使用 retain/release/retainCount/autorelease

        内存管理完全交给编译器去做,所以之前内存相关的函数(retain/release/retainCount/autorelease)不能出现在程序中。Apple的ARC文档中也有下面的说明。

        ARC 有效后,不需要再次使用retain 和 release

        如果我们在程序中使用这些函数,经得到类似下面的编译错误信息。

        生成并持有一个Objective-C对象的时候,往往像下面一样使用NSObject的alloc接口函数。

        NSZone 是什么?NSZone 是为了防止内存碎片而导入的一项措施。Zone 是内存管理的基本单元,系统中管理复数的Zone。系统根据对象的使用目的,尺寸,分配其所属的Zone区域。以提高对象的访问效率,避免不必要的内存碎片。但是,现在的运行时系统(用编译开关 __OBJC2__ 指定的情况下)是不支持Zone概念的。所以,不管ARC是否有效,都不能使用 NSZone。

        不能明示调用dealloc

        不管是否使用ARC,当对象被释放的时候,对象的dealloc函数被调用(就像是C++中对象的析构函数)。在该函数中,需要做一些内存释放的动作。比如,当对象中使用了malloc分配的C语言内存空间,那么dealloc中就需要像下面一样处理内存的释放。

        1234
        void) dealloc{    free(buffer_);}

        又或者是注册的delegate对象,观察者对象需要被删除的时候,也是在dealloc函数中动作。

        1234
        void) dealloc{    [[NSNotificationCenter defaultCenter] removeObserver:self];}

        如果在ARC无效的时候,我们还要像下面一样,调用父类对象的dealloc函数。

        1234
        void) dealloc{    [super dealloc];}

        但是当ARC有效的时候,[super dealloc];的调用已经被编译器自动执行,已经不需要我们明示调用了。如果你在代码中还这样写,难免遇到下面的错误。

        在iPhone开发之深入浅出 (3) — ARC之前世今生中,我们知道如果是 alloc/new/copy/mutableCopy/init 开头的函数,需要将对象所有权返回给调用端。这条规则不管ARC是否有效都应该被遵守。只是 init 开头的函数比较特殊,他只在ARC下有要求,而且异常苛刻。

        init 开始的函数只能返回id型,或者是该函数所属的类/父类的对象类型。基本上来说,init函数是针对alloc函数的返回值,做一些初始化处理,然后再将该对象返回。比如:

        在ARC之下,已经不能在代码中使用 NSAutoreleasePool,我们之前写 main.m 文件的时候,往往像下面这样写。

        123456
        int main(int argc, char *argv[]) {    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];    int retVal = UIApplicationMain(argc, argv, nil, nil);    [pool release];    return retVal;}

        而当ARC有效后,我们需要用@autoreleasepool代替NSAutoreleasePool。

        123456
        int main(int argc, char *argv[]){    @autoreleasepool {        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));    }}

        当编译器看到 @autoreleasepool 定义的块后会自动生成 NSAutoreleasePool 对象,并将需要的对象放入 AutoReleasePool 中,当出方块的定义范围时,pool 中的对象将被释放。

        Objective-C 对象不能作为C语言结构体(struct/union)的成员

        当我们设置ARC有效,并在C语言的结构体中定义Objective-C的对象时,将出现类似下面的编译错误。

        123
        struct Data {    NSMutableArray *array;};
        123
        struct Data {    NSMutableArray __unsafe_unretained *array;};

        这样一来,该内存信息不在编译器内存管理对象内,仅仅是使用而已,没有对象的持有权。当然,对象所有权的持有者需要明确的管理他与该结构体的交互,不要引起不必要的错误3。

        【id】与【void*】之间需要明示cast

        ARC 有效的时候,由于编译器帮我们做了内存管理的工作,所以我们不需要太担心。但是当与 ARC 管理以外的对象类型交互的时候,就需要特殊的转型关键字,来决定所有权的归属问题。

        主要的转型关键字是:

        关键字解释__bridge单纯的类型转换,没有进行所有权的转移__bridge_retained类型转换是伴随所有权传递,转换前后变量都持有对象的所有权__bridge_transfer类型转换伴随所有权转移,被转换变量将失去对象的所有权

        当我们在 Core Foundation 对象类型与 Objective-C 对象类型之间切换的时候,需要把握下面的因素:

        • 明确被转换类型是否是 ARC 管理的对象
          • Core Foundation 对象类型不在 ARC 管理范畴内
          • Cocoa Framework::Foundation 对象类型(即一般使用到的Objectie-C对象类型)在 ARC 的管理范畴内
          • 如果不在 ARC 管理范畴内的对象,那么要清楚 release 的责任应该是谁
          • 各种对象的生命周期是怎样的

            题外话

            Xcode 4.3带来的变化

            最近随着 iOS 5.1 的推出,Xcode也推出了4.3版本。在该版本下,ARC 有效时的属性(@property) 定义的时候,如果不明确指定所有权关键字,那么缺省的就是 strong。而在 Xcode4.2 中,即使 strong 也要显示指定。

            在 Xcode4.2 的时候,针对下面的代码,

            1234567
            // ARC 无效@property (nonatomic, retain) NSString *string;// --->// ARC 有效@property (nonatomic, strong) NSString *string;

            而在 Xcode 4.3 中,我们可以这么做,

            1234567
            // ARC 无效@property (nonatomic, retain) NSString *string;// --->// ARC 有效@property (nonatomic) NSString *string;

            ARC 代码自动变换

            另外,Xcode 4.2开始,增加了旧代码向 ARC 代码自动转换的功能。有兴趣的朋友可以试试。位置是:

            Edit->Refactor->Convert to Objective-C ARC…

            为什么iOS中没有GC

            我们已经知道ARC并不是GC(垃圾回收)了,那么,为什么iOS中不支持该机能呢?还特意搞出个ARC来。以下是我的分析:

          • 消耗CPU时间的处理尽量避免,以节约电池电量
          • GC执行的后,会停掉运行时库;这是最大的心结
          • 嵌入式设备本身内存就不是很大,如果GC不停的在后台运行,执行的频率会很高,严重影响性能
          • UI动画处理是iOS的一大卖点,而有了GC后可能会引起不必要的性能损失

            1.?关于Objective-C对象的解释,可以参考iPhone开发入门(7)— 从C/C++语言到Objective-C语言。

            2.?当然,如果你不写,编译器会用缺省的值代替。具体见iPhone开发之深入浅出 (3) — ARC之前世今生中的描述。

            3.?关于这一点,可以参考iPhone开发之深入浅出 (1) — ARC是什么?一文,明白为什么 __unsafe_unretained 是危险的。


热点排行