swift进阶

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

2020-12-15  本文已影响0人  markhetao

swift进阶 学习大纲

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

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

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

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

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

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

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

image.png
  1. 不管是哪种引用持有的都是原对象(从p到p5内存地址可以看出)
  2. 在每一行执行完后,x/4g打印p对象内存信息,在第二地址上,可以清晰感受到,强引用无主引用引用计数有规律增加,而弱引用没有变化
  • 经过了上面的初体验,我们对强引用无主引用计数的位置有了初步的感受,但弱引用的信息存放不明朗

2. 强引用 & 无主引用

2.1 源码探索

  • swift源码探索过程:
    image.png
  • 现在,我们知道强引用无主引用是在Uint64_t 8位refCount的不同位置。

2.2 引用计数分析

下面通过案例检查一下:

class HTPerson {
    var age  = 10
    var name = "ht"
}

var t = HTPerson()
var t1 = t
var t2 = t
print("end")
image.png
image.png

2.3 强引用计数+1

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

image.png

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

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

image.png

3. CFGetRetainCount计数统计

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

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

image.png

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

image.png

4. 弱引用

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
上一篇下一篇

猜你喜欢

热点阅读