准备
这边文章我们继续讲解内存管理系列文章弱引用.平常的开发中我们经常使用__weak
修饰符来结果开发过程中的循环引用问题。是因为__weak
修饰的对象引用计数不会增加,那么这边文章将从底层源码的角度分析__weak
是如何实现的。
断点调试
断点调试 总是显示反汇编调试信息debug->show->always show disassembly
调用objc_initWeak方法
为什么会调用objc_initWeak?
主要是在LLVM编译的时候,会通过__weak
匹配到objc_initWeak
符号。可以在llvm源码里面验证这一点,如下图
源码实现
在objc源码里搜索objc_initWeak
objc_initWeak
- 首先判断调用的对象是否为空,为空直接返回
- objc_initWeak(&weakObjc,objc),可以直接理解常这样传参
- 调用storeWeak,这个方法时间上以一个高度封装的方法,objc_destroyWeak销毁的时候同样也会调用.这个方法传了模版参数。
//haveOld:为true,表示需要被及时清理
//haveNew:为ture,需要一个新的value来替换
//如果crashifdeallocation 是 ture ,那么如果 newObj 是 deallocating,或者 newObj 的类不支持弱引用,则该进程就会停止。
//如果crashifdeallocation 是 false,那么 nil 会被存储。
storeWeak
进入源码:第一步,根据对象获取oldTable合newTable表
第二步:对表进行加锁,进行线程安全处理,location
应该与 oldObj
保持一致,如果不同,说明当前的 location
已经处理过 oldObj
可是又被其他线程所修改, 保证线程安全,这个判断用来避免线程冲突重处理问题
第三步:确保引用对象的类的初始化已经完成
第四步:如果是旧值,从旧值从弱引用表中移除
第五步:如果是新值插入对应的弱引用表中,然后通过setWeaklyReferenced_nolock
方法标记为对应弱引用对象。
weak_register_no_lock
这个方法主要是将弱引用对象插入到对应的弱引用表。
第一步:判断是否是tagged pointer
小对象或者nil
,如果是直接返回。
第二步:判断对象是否正dealloc
,确保对象可用
第三步:进行添加操作
这里有一个判断,如果之前以前存在对应的entry
,则直接添加。如果没有则创建后在添加。
append_referrer(添加)
第一步:先存储到inline里面 第二步,如果inline存满了,存储到outline,并且把inline数据的也存储到outline中 第三步:outline扩容,达到容量的3/4时候扩容
弱引用的释放
上篇文章 iOS内存管理(Nonpointer_isa+散列表+retain+release)提到过对象的释放,实际上对象有一个弱引用的标识符weakly_referenced
,来区分
objc_clear_deallocating->clearDeallocating
调用weak_clear_no_lock
弱引用的结构图
下面用一张图来总结,弱引用表的结构,便于理解
- 首先有一个全局的变量,里面有8个
sideTable
(真机上)。通过object
对象可以找到对应的sideTabe
。 sideTable
里面有一个weak_table_t
.weak_table_t
里面存储了一个weak_entry_t
的数组,可以通过object拿到对应的weak_entry_t
。一个object对象和一个weak_entry_t
对应- 一个对象可能有多个弱引用,弱引用对象就存储在
weak_entry_t
的inline
或者outline
里面
总过上面的一个结构图我相信大家对弱引用的了解更加全面,这样更有利于我们对源码的理解。