在了解弱引用之前,需要先了解散列表的知识
经常会在oc中使用 __weak typeof(id) weakSelf = self
weakSelf 加入到弱引用表
此时 self 引用计数为 1
weakSelf 引用计数为 2
也就是 加入弱引用表之后,是不影响引用计数的
测试下 weak 引用计数
image.png实际测试结果 与开始的有出入,差别在于 weakObject 的引用计数为3,不是2
那么久从这个区别开始 探究 弱引用
弱引用表
通过汇编查看__weak 底层调用
image.png到libobjc 源码中查看
image.png查看storeWeak(location, newObj)
location 就是weakObjc newObj 为 objc
image.png根据object 获取散列表的过程,在 ios-散列表 中有介绍,具体细节可以去查看
StripedMap[hash(objc指针)] -> SideTable
SideTable.weak_table -> weak_table_t
image.png image.png继续回到 storeWeak 关键逻辑
image.png image.png image.png具体弱引用表结构的访问
-
StripedMap[hash(objc指针)] -> SideTable
-
SideTable.weak_table -> (weak_table_t weak_table)
-
weak_table 取出成员 weak_entries数组 (weak_entry_t *weak_entries )
-
对象地址 通过hash 计算, 得到访问 数组的index
- hash_pointer(对象地址) & (weak_table->mask), 也就是begin
-
weak_entries[index].referent 也就是 weak_entry_t.referent 可以理解为key,
用objc与key比较
-
如果 begin 并未命中
-
index++, index超过边界,取模 从第一个位置又开始,直到与begin相等
-
如果 遍历的次数超过了 weak_table->max_hash_displacement 上限,返回nil
-
如果找到 目标entry 也就是entry.referent == objc, 存储 __weakObjc
-
这里有必要复习下 weak_entry_t 结构
image.png
image.png -
如果 标识 out_of_line_ness == 2, 则用图中的2结构来存储 __weakObjc
-
遍历 inline_referrers数组,找到空位 就存储
-
没找到空位的话,说明 inline_referrers数组已经存满了
-
这个时候选择使用 图中1结构来存储 __weakObjc 新开辟4个空间出来,把 inline_referrers数组中的内容 拷贝到 referrers中
-
1结构中的num_refs 设置为4
-
1结构中的out_of_line_ness 设置标识 REFERRERS_OUT_OF_LINE
-
1结构中的mask 设置为3 主要是为了索引取模用
-
1结构中的max_hash_displacement 设置为0
-
此时 2结构就弃用, 然后继续执行下面的逻辑
-
向 referrers 插入 __weakObjc 时对 __weakObjc(objc_object**)进行hash,结果 & mask, 得到索引index, 也是begin
-
如果 referrers[index] 不空, hash_displacement++ , index = ++index & mask, 直到 index == begin 结束
-
如果hash_displacement > max_hash_displacement, 那么 max_hash_displacement 设置为 hash_displacement
-
referrers[index] = __weakObjc(objc_object**)
-
num_refs++
-
1结构中 num_refs >= mask + 1, 1结构进行两倍扩容
-
mask 由原来的3 变为了 7,也就是 2*4 - 1
-
接着执行 上面的
-
否则 referrers数据备份,创建一个新referrers 进行两倍扩容,备份数据插入 新referrers
-
新referrers 执行 插入 __weakObjc 逻辑
-
释放 旧referrers
-
-
如果未找到 目标entry 也就是没有发现 一个entry满足 entry.referent == objc,创建新的entry
-
newEntry.referent = objc
-
newEntry.inline_referrers[0] = __weakObjc
-
对 newEntry.referent 进行hash ,结果 & weak_table->mask 得到目标index
-
weak_table.weak_entries[index] = newEntry
-
weak_table.num_entries++
分析 __weakObj 引用计数为3 非2的问题
StripedMap[hash(objc)].weak_table.weak_entries[hash(objc)].referent
弱引用表中 referent 指向objc 堆内存空间,是不是就导致引用计数 +1 ?
image.png image.png先获取局部变量obj,传递给 弱引用表,此时obj引用计数为2
但obj为局部变量,离开作用于,release,引用计数1
此处不需要纠结,应该是跟编译器有关
我们重点放在 弱引用表结构 及存储逻辑上
拓展
image.png image.png实现 _setWeaklyReferenced 方法的话,可以做自定义处理
网友评论