1、使用场景
weak常用于修饰代理属性,以防止循环引用导致内存泄漏,__weak与weak基本相同,只不过一个用于修饰变量,主要用于防止block中的循环引用,一个用于修饰属性。
2、weak的原理解析
Runtime在注册某个类时,相应的会维护一个该类的weak表,该weak表实际上是一个hash表,其中
- key:所指对象的地址
- value:weak指针的地址数组(注意,这里是一个数组),weak指针地址中的值正是所引用对象的地址
哈希表相对于数组结构来说,查找速度非常快,更重要的是插入或删除某个元素时,数组需要移动大量的元素,而哈希表则不需要,正因为如此,ARC机制下的引用计数表也设计成hash表
- 不难理解,一个对象可以设置多个weak属性,每新增一个weak属性时,都会在其hash表中对应的value数组中添加一个元素,所以对于一个键值,可以注册多个变量的地址。
- 当一个对象调用release方法即将销毁时,会调用objc_clear_deallocating函数,此函数会将这个对象的地址作为key去当前对象所属类的hash表中去查找,就能很高效的获取到指向该对象的weak指针的地址数组,随后将获取到的指针数组中的元素全部置为nil,同时删除hash表中对应的key-value键值对。
3、weak实现代码解析
以__weak修饰变量为例
id __weak weakObj = tempObj;
在编译的时候会转化成
id weakObj;
objc_initWeak(&weakObj, tempObj);
objc_destoryWeak(&weakObj);
编译器通过objc_initWeak函数初始化__weak修饰的变量,当变量的作用域结束之后,再通过objc_destoryWeak函数释放该变量。objc_initWeak函数主要是初始化weakObj对象,然后将weakObj引用tempObj对象,代码如下:
weakObj = 0;
objc_storeWeak(&weakObj,tempObj);
objc_destoryWeak函数的操作实际上是:
objc_storeWeak(&weakObj,0);
即让weakObj指针指向的内容为空。
对象的释放步骤:
- objc_release
- 引用计数为0,执行dealloc方法
- _objc_rootDealloc
- objc_dispose
- objc_destructInstance
- objc_clear_deallocating
对象被废弃时,最后调用objc_clear_deallocating,执行的相关操作为:
- 从weak表中获取废弃对象的地址为键值的记录
- 将包含在记录中的所有附有__weak修饰符变量的地址,赋值为nil
- 从weak表中删除该记录
- 从引用计数表中删除废弃对象的地址为键值的记录