《OC高级编程》之自动引用计数(三)
ARC实现
ARC 由 clang (LLVM 编译器) 3.0 以上和 objc4 Objective-C 运行时库 493.9 以上实现
__strong 修饰符
2 次调用 objc_msgSend 方法(alloc 和 init),变量作用域结束时通过objc_release 释放对象,编译器自动插入 release
用除 alloc/new/copy/mutableCopy 外方法时,release 之前调用了objc_retainAutoreleasedReturnValue 函数,与之相对的是objc_autoreleaseReturnValue 函数,用于 alloc/new/copy/mutableCopy 外等返回对象的实现,返回注册到 autoreleasepool 中的对象,但会检查使用该函数的方法或调用方的执行命令列表,如果之后紧接着调用objc_retainAutoreleasedReturnValue 函数,那么就不将对象注册到autoreleasepool,而是直接传递,即便不注册到 autoreleasepool,也可以准确获取对象,这一过程达到了最优化
__weak 修饰符
{
id __weak obj1 = obj;
}
/* 编译器的模拟代码 */
id obj1;
obj1 = 0;
objc_storeWeak(&obj1, obj);
objc_storeWeak(&obj1, 0);
objc_storeWeak 函数把第二个参数的赋值对象的地址作为键值,将第一参数的附有 __weak 修饰符的变量的地址注册到 weak 表。如果第二参数为 0,则把变量从weak 表删除
weak 表与引用计数表相同,作为散列表实现。将废弃对象的地址作为键值进行检索,就能高效获取对应的 __ weak 变量的地址。由于一个对象可赋值给多个__weak 变量,所以对一个键值,可注册多个变量地址
释放对象时,最后调用的 objc_clear_deallocating 函数动作如下:
- 从 weak 表获取废弃对象的地址作为键值的记录
- 将包含在记录中的所有附有 __weak 修饰符变量的地址,赋值为 nil
- 从 weak 表删除该记录
- 从引用计数表中删除废弃对象的地址为键值的记录
可见,如果大量使用 __weak,会消耗相应的 CPU,所以只在需要避免循环引用时使用
id __weak obj = [[NSObject alloc] init];
id __unsafe_unretained obj = [[NSObject alloc] init];
//编译器警告
[[NSObject alloc] init];
//ARC 无效时会发生内存泄漏
(void)[[NSObject alloc] init];
//避免警告
//ARC 有效时,编译器生成并立即调用 objc_release 函数,不会造成内存泄漏
//可以调用被立即释放的对象的实例方法,调用完就被释放
访问附有 __weak 修饰符的变量时,调用了 objc_loadWeakRetained 函数和objc_autorelease 函数
- objc_loadWeakRetained 函数取出附有 __weak 变量所引用的对象并 retain
- objc_autorelease 函数将对象注册到 autoreleasepool 中
在大量使用 __ weak 变量时,注册到 autoreleasepool 中对象也大量增加,最好先暂时赋值给附有 __strong 变量再使用
独自实现引用计数的类大多不支持 __weak,当 allowsWeakReference/retainWeakReference 方法返回 NO 时,也不能使用,该变量将变为 nil
__autoreleasing 修饰符
没啥说的,就调用 objc_autorelease 函数
引用计数
用 _objc_rootRetainCount 函数获取引用计数值(不完全可信
强引用持有 +1,弱引用不变,向 autoreleasepool 注册+1,访问 __weak +1