ios-弱引用

作者: erlich | 来源:发表于2022-08-12 06:32 被阅读0次

    在了解弱引用之前,需要先了解散列表的知识

    ios-散列表

    经常会在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 方法的话,可以做自定义处理

    相关文章

      网友评论

        本文标题:ios-弱引用

        本文链接:https://www.haomeiwen.com/subject/jsbtgrtx.html