weak引用表,是一张hash表。key为对象的地址,value为所有指向该对象的弱引用指针。
- 实现过程,objc_initWeak
- SideTable
- weak使用注意事项:如果大量使用weak修饰符修饰的变量是,最好先赋值给__strong修饰符变量再使用。至于原因下文会详细说明。
- weak引用表的释放
概况
1 | id obj = [NSObject new]; |
编译器编译后:
1 | objc_initWeak(&obj1, obj); |
通过objc_initWeak 初始化objc1变量,变量objc1超出作用于后通过objc_destroyWeak释放objc1.
objc_initWeak初始化
1 | id objc_initWeak(id *location, id newObj) |
storeWeak函数 Update a weak variable. 更新弱引用变量,忽略多线程资源竞争逻辑,主要分为三步:
- 声明新旧两个SideTabel对象,分别代表旧的weak引用表与新的weak引用表
- 如果weak引用已经有所指向,则将weak引用从旧对象的weak引用表中清除
- 如果weak引用有新的指向,则将weak引用的注册到新对象的weak引用表中
haveOld 弱引用是否已经有所指向
haveNew 是否有新的指向
CrashIfDeallocating 执行方法时发生Deallocate是否Crash
1 | template <HaveOld haveOld, HaveNew haveNew, |
objc_destroyWeak 函数,
1 | void |
weak引用表的释放
先上一张图,一图胜千言
objc_object::clearDeallocating()函数
1 | void |
来看看weak_clear_no_lock函数的实现
1 | void |
这就很清楚了,主要步骤:
- 从weak表中,获取referent_id的 weak引用实体 也就是weak_entry_t
- 获取weak_entry_t中的referrers, 也就是所有引用了referent_id 的弱引用对象,这是一个数组
- 遍历数组,将所有弱引用对象赋值为 nil
- 将entry从weak引用表中移除
回答刚开始提出的问题:为什么如果大量使用weak修饰符修饰的变量时,最好先赋值给strong修饰符变量再使用?
因为weak修饰符修饰的变量生命周期分为:创建、存储、销毁三个过程,其中还涉及Hash以及数组的遍历等操作。而strong修饰符只是简单引用计数+1和-1,相对于strong修饰符来说weak修饰符更消耗性能。
以上不是主要原因,因为使用weak修饰符修饰的变量时,即是使用注册的autoreleasepool中的变量,这是为了确保weak变量在使用过程中,不会被置为nil。而如果大量使用weak变量的话,就会导致weak变量多次注册到autoreleasepool,为了避免这种情况,最好先赋值给__strong 变量再使用