iOS-ARC

作者: doudo | 来源:发表于2017-08-31 00:42 被阅读5次

    本文的内容包括

    一、所有权修饰符
    二、ARC的基本规则
    三、ARC的实现

    ARC中仍然是通过引用计数来管理内存,这个本质没有变。只是,不需要我们手动的写代码去管理内存了,编译器自动帮助我们管理“引用计数”相关的部分。那么ARC是通过什么方式来帮我们管理内存的呢?

    ARC是通过编译期运行期两部分来处理的:

    • 编译期,编译器不是通过添加retain/release/autorelease这些方法,而是会直接调用更底层的C语言函数(如objc_retain)。
    • 运行期,ARC也包含运行期组件。比如,某些类方法返回对象前,为其执行了autorelease操作,而大多数情况,我们会对返回的对象保留,比如:_myPerson = [Person personWithName:@"Kobe"];
      那么其实就相当于先执行了一个autorelease,然后又retain了一下。ARC可以在运行期检测到这一多余的操作,也就是autorelease后紧跟retain。那么会它们两个会被改为调用objc_autoreleaseReturnValueobjc_retainAutoreleasedReturnValueobjc_autoreleaseReturnValue会检查后边是否紧接着调用objc_retainAutoreleasedReturnValue,如果是,就不将返回的对象注册到autoreleasepool中而直接传递,省略了autorelasepool注册,实现了最优化。

    一、所有权修饰符

    1. __strong修饰符

    如它的名字一样,__strong表示对对象的强引用。持有强引用的变量在超出其作用域的时候被废弃,随着强引用的失效,引用的对象会随之释放。

    知识点:

    1. __strong修饰符是id类型和对象类型默认的所有权修饰符,所以一般不写__strong。
    2. __strong和__weak和 __autoreleasing修饰符一样,可以将附有这些修饰符的自动变量初始化为nil。

    2. __weak(iOS5以上才有,之前用__unsafe_unretained)

    如它的名字一样,__weak修饰符表示对对象的弱引用。不持有对象。

    知识点:

    1. 可以避免循环引用。
    2. 变量指向的对象被销毁了,变量也会自动置空为nil。

    3. __unsafe_unretained

    如它的名字一样,它是不安全的所有权修饰符。

    知识点:

    1. 和__weak一样表示弱引用,不持有对象,但不会置nil,这也正是不安全的原因。
    id __unsafe_unretained obj1 = nil;
        {
            id obj0 = [[NSObject alloc] init];
            obj1 = obj0;
            NSLog(@"A:%@",obj1);
        }
        NSLog(@"B:%@",obj1);
    

    输出A和B虽然是一样的地址,但是此时B处obj1已经是野指针了。

    4. __autoreleasing

    通过给对象附加__autoreleasing修饰符 来替代调用autorelease方法,把对象注册到autoreleasepool中。

    具体使用情况,在这里

    二、规则

    1. 不能使用retain/release/retainCount/autorelease

    2. 用@autoreleasepool{}代替NSAutoreleasePool对象

    3. 不要显式的调用dealloc

    多数情况下在dealloc中删除已注册的代理或观察者。不用书写[super dealloc],因为ARC已经自动处理了。

    4. 必须遵守内存管理的方法命名规则

    1. alloc/new/copy/mutableCopy,以上述名称开始的方法在返回对象时,必须返回给调用方所应当持有的对象。这在ARC下,依然没有变。
    2. ARC下追加了一条命名规则:
    • init,以init开头的方法,必须是实例方法,并且返回对象,类型应为id或该类的的对象类型,抑或是超类或子类型。该方法基本上只是对alloc方法返回的对象进行初始化操作并返回。
      注:initialize方法并不包含在上述命名规则里。

    5. 对象类型不能作为结构体的成员

    因为ARC把内存管理的工作分配给了编译器,所以编译器必须能够知道并管理对象的生命周期。例如C语言的自动变量(局部变量)可以使用该变量的作用域来管理。但是对于结构体成员来说,是无法实现的。

    6. 显式转换id和void*

    id obj = [[NSObject alloc] init];
    void *p = (__bridge void *)obj;
    

    但是,__bridge安全性与__unsafe_unretained类似,甚至更低。很容易造成野指针导致崩溃。
    __bridge还有另外两种转换,__bridge_retained和__bridge_transfer

    1. Core Foundation 对象类型不在 ARC 管理范畴内,需要自己管理.
    2. __bridge只做类型转换,但是不修改对象(内存)管理权,原来是ARC管理的还用ARC,原来MRC管理的继续用MRC
    3. __bridge_retained(也可以使用CFBridgingRetain)将Objective-C的对象转换为Core Foundation的对象,同时将对象(内存)的管理权交给我们,后续需要使用CFRelease或者相关方法来释放对象;
    4. __bridge_transfer(也可以使用CFBridgingRelease)将Core Foundation的对象转换为Objective-C的对象,同时将对象(内存)的管理权交给ARC。

    5. 不能使用NSAllocateObject/NSDeallocObject

    6. 不能使用区域(NSZone)

    三、ARC的实现

    1. __strong实现

    赋值给附有__strong修饰符的变量在实际的程序中是怎样运行的呢?

    {
           id __strong obj = [[NSObject alloc] init];
       }
    

    编译器在超出作用域时自动插入了release。

    {
            id __strong obj = [NSMutableArray array];
        }
    

    执行,alloc/new/copy/mutableCopy之外的方法,如array类方法:
    像代码这样,返回注册到autoreleasepool中对象的方法使用了objc_autoreleaseReturnValue,如果其后紧接着调用objc_retainAutoreleasedReturnValue(),那么就不将返回的对象注册的autoreleasepool中而直接传递。通过这两个方法,优化了程序运行。

    2.__weak的实现

    id __strong obj = [[NSObject alloc]init];
    id __weak obj1 = obj;
    

    objc_initWeak函数中的weak_register_no_lock()把赋值对象obj的地址作为键值,通过哈希查找找到weak弱引用表中对应的数组,将附有__weak修饰符变量的指针添加到数组中。如果没有找到数组,表示是第一个weak指针,则新建一个数组。

    对象在被废弃时dealloc方法中会调用object_dispose函数,该方法内部会通过调用weak_clear_no_lock()

    1. 通过哈希查找从weak表中获取废弃对象地址作为键值的记录是一个数组
    2. 将包含在数组中的所有附有__weak修饰符的变量地址,遍历数组赋值为nil。
    3. 从weak表中删除该记录。
    4. 从weak表中删除该键值。

    所以,如果大量使用__weak修饰符的变量,则会消耗相应的cpu资源。良策是只在需要避免循环引用的时候使用__weak。

    3. __autoreleasing修饰符的实现

    objc_autorelease

    4. 引用计数

    可以通过_objc_rootRetainCount(obj)来获取对象的引用计数值。但实际上并不能完全信任该函数取得的值。对于已经释放的对象以及不正确的对象地址,有时也返回1。另外,在多线程中使用它,因为存在竞态条件的问题,所以取得的数值也不一定完全可信。当然它在调试中还是比较有用的。

    相关文章

      网友评论

          本文标题:iOS-ARC

          本文链接:https://www.haomeiwen.com/subject/fkrfjxtx.html