美文网首页swift进阶
swift进阶七:引用计数(Strong、Unowned、Wea

swift进阶七:引用计数(Strong、Unowned、Wea

作者: markhetao | 来源:发表于2020-12-15 21:43 被阅读0次

    swift进阶 学习大纲

    上一节,介绍了方法调度 & @objc & 指针。本节,我们就探究较难的引用计数,将从以下4个方面探索:

    1. Swift三大引用计数(strong、unowned、weak)
    2. 强引用 & 无主引用
    3. CFGetRetainCount计数统计
    4. 弱引用

    swift中的引用计数OC一致,都是采用ARC(自动引用计数)管理。

    OC的对象都是以objc_object为模板创建,其中首元素isa

    • 开启指针优化(nonpointer): 在isa存储引用计数,可使用散列表进行拓展存储
    • 未开启指针优化: 直接使用散列表进行存储
    • swift引用计数:
    • swift对象都是以HeapObject为模板创建,其中HeapObject的模板中第二个元素,是refCount引用计数属性,该属性记录了strong(强引用计数)和unowned(弱引用计数)等信息。
    • weak修饰对象,会另外生成WeakReference对象,内部HeapObjectSideTableEntry散列表类在原heapObject类的基础上,重新记录了refCount(管理strongunowned引用计数)并新增了weakBits弱引用计数。

    1. Swift三大引用计数(strong、unowned、weak)

    首先,我们先通过案例,体验一下Swift对象的三种引用类型:

    • strong(默认强引用类型)、unowned(无主引用类型)、weak(弱引用类型)
    image.png
    1. 不管是哪种引用持有的都是原对象(从p到p5内存地址可以看出)
    2. 在每一行执行完后,x/4g打印p对象内存信息,在第二地址上,可以清晰感受到,强引用无主引用引用计数有规律增加,而弱引用没有变化
    • 经过了上面的初体验,我们对强引用无主引用计数的位置有了初步的感受,但弱引用的信息存放不明朗
    • 下面,我们通过案例SIL中间代码Swift源码汇编等方式,一点点揭开他们的面纱😃

    2. 强引用 & 无主引用

    2.1 源码探索

    • 当前以默认initialized方式进行初始化,分析HeapObject对象引用计数
    • swift源码探索过程:
      image.png
    • refCount内存布局
      image.png
    • 现在,我们知道强引用无主引用是在Uint64_t 8位refCount的不同位置。

    2.2 引用计数分析

    下面通过案例检查一下:

    • 创建一个Swift命令行项目
    class HTPerson {
        var age  = 10
        var name = "ht"
    }
    
    var t = HTPerson()
    var t1 = t
    var t2 = t
    print("end")
    
    • 【尝试一】: 在t1处断点t对象强引用无主引用计数都为1
    image.png
    • 【尝试二】: 在t2处断点t对象强引用计数2无主引用计数1
    image.png

    2.3 强引用计数+1

    • 还是以上面测试代码为例,我们结合SIL中间代码和Swift源码分析:

    【情况一】仅创建对象,默认强引用计数1

    image.png

    【情况二】进行一次引用强引用计数2SIL中可以看到copy_addr汇编可以看到使用swift_retain,在swift源码中可以知道执行路径为:

    swift_retain->refCounts.increment(1)->incrementStrongExtraRefCount->强引用计数+1

    image.png

    3. CFGetRetainCount计数统计

    • CFGetRetainCount会在执行前,对对象进行strong_retain操作,在执行后,完成release_value操作。
      所以swiftCFGetRetainCount打印的强引用计数比原引用计数多1

    注意:swift中,在lldbp打印内存,会引用计数+1,影响影响CFGetRetainCount结果
    (断点,p打印一次或多次,x/4g在内存信息中可看到引用计数明显变化)

    【情况一】不打印,无retain和release

    image.png

    【情况二】打印一次CFGetRetainCount,执行前strong_retain +1,执行完release_value -1

    image.png

    4. 弱引用

    • 我们知道swift是使用ARC(自动引用计数管理)的。如果产生循环引用,我们必须有弱引用机制去打破循环

    swift中的弱引用,使用weak修饰。与OC不同的是:

    • OC:
      弱引用计数是存放在全局维护散列表中,isa中会记录是否使用了散列表
      引用计数0时,自动触发dealloc,会检查清空当前对象散列表计数

    • swift:
      弱引用计数也是存放在散列表中,但这个散列表不是全局的。

      • 如果对象没有使用weak弱引用,就是单纯的HeapObject对象,没有散列表
      • 如果使用weak弱引用,会变为WeakReference对象。这是一个Optionl(可空对象)。其结构中自带散列表计数区域。
        swift散列表refCount无关联。当强引用计数0时,不会触发散列表的清空。而是在下次访问发现当前对象不存在(为nil)时,会清空散列表计数

    下面,我们通过案例源码来分析swift弱引用WeakReference对象内存结构

    案例:

    • 可以发现:
      weak修饰前,p对象是HeapObject类型,可从refCount中看出强引用计数无主引用计数
      weak修饰后,p对象的类型变了

      image.png
    • 可以看到weak修饰p1对象,变成了optinal可选值
      (不难理解,weak修饰对象改变原对象的引用计数,只是一层可空状态

      image.png
    • 断点汇编可以看到swift_weakInit初始化,swift_weakDestroy释放。

      image.png
    • 进入swift源码,搜索swift_weakInit

    image.png
    • 常规对象弱引用对象区别:
    image.png
    • 现在,我们已知道弱引用实际上是WeakReference对象,信息都存储在side弱引用表中,可仿照getSideTable函数左移3位得到side散列表地址。读取弱引用信息

    我们回到上面案例

    image.png
    • 了解结构后,关于弱引用引用计数+1-1释放都在WeakReference类中有介绍,可以自行了解。

    相关文章

      网友评论

        本文标题:swift进阶七:引用计数(Strong、Unowned、Wea

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