ARC的实现原理

1、ARC的实现原理

我们都知道,ARC是编译器特性,程序在编译的时候,编译器帮我们在合适的地方插入retain、release等代码以管理对象的引用计数,从而达到自动管理对象生命周期的目的。

但是只有编译器是无法单独完成这一工作的,还需要OC运行时库的配合协助,因此ARC的实现工具主要包括:

  1. LLVM编译器(clang 3.0以上)
  2. OC运行时库493.9以上

2、__strong修饰符

使用 __strong 修饰变量的程序究竟是如何运行的

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

转换后的模拟源代码为

/*编译器的模拟代码*/
id object = objc_msgSend(NSObjct,@selector(alloc));
objc_msgSend(object,@selector(init));
objc_release(object);

对象变量生成时,allocinit分别进行了方法调用,对象变量作用域结束时调用objc_release方法释放对象变量,虽然ARC情况下不能使用release方法,但是由此可见编译器编译时在合适的地方插入了release

在使用alloc、new、copy、mutableCopy以外的方法生成对象变量方法时会有什么不同

{
    id __strong object = [NSMutableArray array];
}

调用array的类方法转换后如下

{
    /*编译器的模拟代码*/
    id object = objc_msgSend(NSMutableArray,@selector(array));
    objc_retainAutoreleasedReturnValue(object);
    objc_release(object);
}

中间的objc_retainAutoreleasedReturnValue(object)函数与之前的不同,它主要是起何作用的呢?

objc_retainAutoreleasedReturnValue(object)函数的作用:最优化程序运行

自己持有(retain)对象的函数,但它持有的应为返回注册在autoreleasepool中对象的方法或函数的返回值。

objc_retainAutoreleasedReturnValue函数与objc_autoreleasedReturnValue是成对出现的,现在看看NSMutableArray类的array类方法的编译器实现

    + (id)array {
        return [[NSMutableArray alloc] init];
    }

转换后的源代码

    + (id)array {
         /*编译器的模拟代码*/
        id obj = objc_msgSend(NSMutableArray,@selector(alloc));
        objc_msgSend(obj,@selector(init));
        return objc_autoreleaseReturnValue(obj);
    }

通过objc_autoreleaseReturnValue函数将对象注册在自动释放池autoreleasepool中并返回,但是与objc_autorelease函数不同的是,objc_autoreleaseReturnValue函数一般不仅限于注册对象到autoreleasepool中去。

objc_autoreleaseReturnValueobjc_retainAutoreleasedReturnValue的配合使用,可以不将对象注册到autoreleasepool中而直接传递,达到最优化

objc_autoreleaseReturnValue函数会检查使用该函数的方法或者函数的调用方的执行命令列表,如果调用方在调用该函数或方法之后,紧接着调用了objc_retainAutoreleasedReturnValue函数,那么将不再将对象注册到autoreleasepool中去,而直接将对象传递给调用方。

相比于objc_retain函数来说objc_retainAutoreleasedReturnValue函数在返回一个即使没有注册到autoreleasepool中的对象,也能正确的获取对象。

3、__weak修饰符

weak解析可参考下面这篇链接:
weak属性修饰词解析

这里主要讲解下__weak__unsafe_unretained的区别

{
    id _weak object = [[NSObject alloc] init];
}

转换后的模拟源代码为

{
    /* 编译器的模拟代码 */
    id object;
    id tmp = objc_msgSend(NSObject, @selector(alloc));
    objc_msgSend(tmp, @selector(init));
    objc_initWeak(&object, tmp);
    objc_release(tmp);
    objc_destoryWeak(&object);
}

自己生成并且持有的对象通过objc_initWeak函数赋值给__weak修饰符的变量,但是编译器判断并没有对其进行持有,因此该对象通过objc_release函数被释放和废弃。
随后通过objc_destoryWeak将引用废弃对象的附有__weak 修饰符的变量置为nil

{
    id __unsafe_unretained object = [[NSObject alloc] init];
}

转换后的模拟源代码为

 {
     /*编译器的模拟代码*/
    id object = objc_msgSend(NSObject, @selector(alloc));
    objc_msgSend(object, @selector(init));
    objc_release(tmp);
}

可见通过__unsafe_unretained修饰的变量引用了对象但是并不持有对象,对象在释放和废弃后,并没有调用被__unsafe_unretained修饰的变量的objc_destoryWeak函数,因此该对象的悬垂指针被赋值给变量object,导致引用变量object时发生奔溃。

带有__weak修饰符的变量,其引用的对象已经注册到autoreleasepool

如果不是直接赋值,而是通过使用__weak修饰符来引用变量时

{
    id __weak object = obj;
    NSLog(@"%@",object);
}

转换后的模拟源代码为

/*编译器的模拟代码*/
{
    id object;
    objc_initWeak(&object, obj);
    id temp = objc_loadWeakRetained(&object);
    objc_autorelease(temp);
    NSLog(@"%@", temp);
    objc_destoryWeak(&object);
}

明显增加了objc_loadWeakRetainedobjc_autorelease函数调用,他们的主要作用是:

  1. objc_loadWeakRetained函数取出__weak修饰符变量引用的对象并且retain
  2. objc_autorelease函数将引用的对象注册到autoreleasepool

因此,使用__weak修饰符引用的对象都被注册到autoreleasepool中,在@autoreleasepool块结束之前都可以放心使用,大量使用__weak修饰符的变量,导致注册到autoreleasepool中的对象也大量地增加。所以在使用__weak修饰符引用的变量时,最好先暂时用__strong修饰符的变量进行引用后再使用。

2种不能使用__weak修饰符的情况

  • 重写了retain/release的类,例如NSMachPort
  • allowsWeakReference/retainWeakReference实例方法返回NO时

4、__autoreleasing修饰符

__autoreleasing修饰符变量引用的对象,相当于在MRC情况下调用对象的autorelease方法

对于alloc、new、copy、mutableCopy如何实现的

@autoreleasepool{
    id __autoreleasing object = [[NSObject alloc] init];
}

转换后的模拟源代码为

{
    /*编译器的模拟代码*/
    id pool = objc_autoreleasePoolPush();
    id object = objc_msgSend(NSObjct, @selector(alloc));
    objc_msgSend(object, @selector(init));
    //调用autorelease方法
    objc_autorelease(object);
    id pool = objc_autoreleasePoolPop();
}

NSMutableArray类种的array方法如何实现autorelease功能

@autoreleasepool{
    id __autoreleasing object = [NSMutableArray array];
}

转化后的模拟源代码为:

{
    /*编译器的模拟代码*/
    id pool = objc_autoreleasePoolPush();
    id object = objc_msgSend(NSMutableArray, @selector(array));
    objc_retainAutoreleasedReturnValue(object);
    //调用autorelease方法
    objc_autorelease(object);
    id pool = objc_autoreleasePoolPop();
}

除了持有对象的方法从alloc变成了objc_retainAutoreleasedReturnValue函数,但是注册到autoreleasepool的方法没有变化,都是调用了objc_autorelease函数。

5、引用计数

extern uintptr_t _objc_rootRetainCount();可获取ARC情况下对象的引用计数

仅用于调试,对于已释放的对象或者不正确的对象地址,有时也返回“1”,此外在多线程情况下使用对象的引用计数数值,因为竟态条件的问题,取得的数值不一定完全可信。

你可能感兴趣的:(iOS移动开发)