--- layout: post title: 为什么使用weak修饰的变量会自动设置为nil subtitle: 在不断填坑中前进。。 date: 2017-06-26 author: 三十一 header-img: img/post-bg-nextgen-web-pwa.jpg header-mask: 0.3 catalog: true tags: - 内功 - iOS ---
为什么使用weak修饰的变量会自动设置为nil
Objective-C高级编程 iOS与OS X多线程和内存管理 读书笔记
为什么使用weak修饰的变量会自动设置为nil
我们知道:
- 当使用
__weak
修饰符的变量的 引用对象 被废弃时,则将nil赋值给该变量。 - 使用附有
__weak
修饰符的变量,即是使用注册到autoreleasepool
中的对象。
系统是如何实现的?
先看下面的代码:
{
id __weak obj1 = obj;
}
经过编译器会装换为下面的代码;
id obj1;
objc_initWeak(&obj1,obj);
objc_destroyWeak(&obj1);
其中 objc_initWeak
函数初始化附有 __weak
修饰符的变量,在变量作用域结束后通过 objc_destroyWeak
函数释放该变量。
objc_initWeak
实现:
obj1 = 0;
objc_storeWeak(&obj1,obj);
objc_destroyWeak
实现:
objc_storeWeak(&obj1,0);
所以以上的代码与下面的源代码相同:
id obj1;
obj1 = 0;
objc_storeWeak(&obj1,obj);
objc_storeWeak(&obj1,0);
objc_storeWeak
函数把第二个参数的赋值对象的地址作为键值,将第一个参数的附有 __weak
修饰符的变量的地址注册到 weak
表中。
*{obj:&obj1} 等同 {“key”:”Value”}*
weak
表 是作为一个散列表实现的。
当 变量 废弃时,使用该对象的地址作为键进行查找,就能快速的获取对应的 __weak
修饰符的变量的地址。另外,由于一个对象可以同时赋值给多个 附有 __weak
修饰符的变量中,所以对于一个键,可注册多个变量的地址。
释放对象时,废弃谁都不持有的对象的同时,程序的动作是怎么样的呢?
- objc_release
- 因为引用计数为0所以执行 dealloc
- _onjc_rootDealloc
- objc_dispose
- objc_destrucInstance
- objc_clear_deallocating
对象废弃时 最后调用的 objc_clear_deallocating
函数动作如下:
- 从
weak
表中获取废弃对象的地址为键值的记录 - 将包含在记录中的所有附有
__weak
修饰符变量的地址,赋值为nil
- 从
weak
表中删除该记录 - 从引用计数表中删除废弃对象的地址为键值的记录。
所以如果附有 __weak
修饰符的变量 所引用的对象废弃后,将 nil 赋值给改变量的步骤是在这里实现的。
由此可知,如果大量的使用附有 __weak
修饰符的变量,会消耗对应的 CPU
资源。良策是只在需要避免循环引用时使用 __weak
修饰符。
最后总结
简单来说就是:
- 使用 __weak 修饰符的变量 A ,A 引用变量 B ,这时系统会把 B 的地址作为Key ,A的地址作为 Value ,存储在一个系统的散列表中;
- 当对象 B 被释放时,会先调用 release 然后引用计数为0;
- 然后调用 dealloc ;
- 接着会调用一个名为
objc_clear_deallocating
的函数,这个函数的作用就是查找以 B 的地址为 key 的 Value,然后将改这些 Value 赋值为 nil,在把这些 Key 、Value 从散列表中删除; - 最后在引用计数表中将以 B 的地址为键值的记录删除。