我们都知道,ARC是编译器特性,程序在编译的时候,编译器帮我们在合适的地方插入retain、release等代码以管理对象的引用计数,从而达到自动管理对象生命周期的目的。
但是只有编译器是无法单独完成这一工作的,还需要OC运行时库的配合协助,因此ARC的实现工具主要包括:
使用 __strong 修饰变量的程序究竟是如何运行的
{
id __strong object = [[NSObject alloc] init];
}
转换后的模拟源代码为
/*编译器的模拟代码*/
id object = objc_msgSend(NSObjct,@selector(alloc));
objc_msgSend(object,@selector(init));
objc_release(object);
对象变量生成时,alloc和init分别进行了方法调用,对象变量作用域结束时调用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_autoreleaseReturnValue与objc_retainAutoreleasedReturnValue的配合使用,可以不将对象注册到autoreleasepool中而直接传递,达到最优化
objc_autoreleaseReturnValue函数会检查使用该函数的方法或者函数的调用方的执行命令列表,如果调用方在调用该函数或方法之后,紧接着调用了objc_retainAutoreleasedReturnValue函数,那么将不再将对象注册到autoreleasepool中去,而直接将对象传递给调用方。
相比于objc_retain函数来说objc_retainAutoreleasedReturnValue函数在返回一个即使没有注册到autoreleasepool中的对象,也能正确的获取对象。
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_loadWeakRetained与objc_autorelease函数调用,他们的主要作用是:
因此,使用__weak修饰符引用的对象都被注册到autoreleasepool中,在@autoreleasepool块结束之前都可以放心使用,大量使用__weak修饰符的变量,导致注册到autoreleasepool中的对象也大量地增加。所以在使用__weak修饰符引用的变量时,最好先暂时用__strong修饰符的变量进行引用后再使用。
2种不能使用__weak修饰符的情况
__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函数。
extern uintptr_t _objc_rootRetainCount();可获取ARC情况下对象的引用计数
仅用于调试,对于已释放的对象或者不正确的对象地址,有时也返回“1”,此外在多线程情况下使用对象的引用计数数值,因为竟态条件的问题,取得的数值不一定完全可信。