Swift(十五)-内存管理之引用计数
内存管理
Swift
语言延续了和Objective-C
语言一样的思路进行内存管理,都是采用引用计数
的方式来管理实例的内存空间;
自动引用计数
自动引用计数Automatic Reference Counting
也就是我们常说的ARC
,其是Objective-C
语言与Swift
语言中解决内存管理问题的一种手段;在开发过程中,对类实例
进行不当的引用将会造成内存泄漏
,当内存泄漏
积累到一定程度就会给应用程序带来灾难性的后果;
Swift中关于引用计数的操作
我们创建一个Person
类如下:
class Person { var age: Int = 20 var name: String = "zhangSan" } 复制代码
接下来,我们来打印分析一下Person
的实例的内存指针:
我们在之前的分析中已经知道,其第二个8
字节存储的是refCounts
,也就是0x3
;在源码HeapObject.h
文件中,我们可以找到HeapObject
结构体中关于refCounts
的定义:
分析InlineRefCounts
可以发现,其是一个接收InlineRefCountBits
泛型参数的模板类RefCounts
:
typedef RefCounts<InlineRefCountBits> InlineRefCounts; 复制代码
根据RefCounts
的定义我们发现,其实质上是在操作我们传递的泛型参数RefCountsBits
,而RefCounts
其实是对引用计数
的包装,而引用计数
的具体类型,取决于传递的参数类型InlineRefCountBits
;
我们继续分析InlineRefCountBits
的定义:
typedef RefCountBitsT<RefCountIsInline> InlineRefCountBits; 复制代码
其也是一个模板函数,而RefCountIsInline
其实就是true
:
enum RefCountInlinedness { RefCountNotInline = false, RefCountIsInline = true }; 复制代码
那么,我们可以知道,引用计数最终操作的类其实是RefCountBitsT
:
我们分析RefCountBitsT
的过程中,发现只有一个bits
属性,而该属性是由RefCountBitsInt
的Type
属性定义的;而根据Type
的定义,我们发现Type
其实是一个uint64_t
类型,是一个64位
的位域信息:
typedef uint64_t Type; 复制代码
至此,我们可以得到结论:
引用计数
本质上是一个64位
的位域信息,在这64位
信息中存储了引用计数
的相关信息;之所以这么做是为了将其抽象出来,提高代码复用率;
那么,问题来了,我们创建一个新的实例对象时,他的引用计数是多少呢?从源码中我们找到HeapObject
的初始化方法:
从HeapObject
的初始化实现中,我们找到了refCounts
的赋值操作:
给refCounts
赋值了Initialized
,我们继续分析发现Initialized
是一个枚举类型Initialized_t
:
而根据源码显示,一个新的实例被创建时,传入的是RefCountBits(0,1)
,而RefCountBits
对应我们上边分析过的RefCountBitsT
,接下里,我们在源码中找到RefCountBitsT
的初始化:
此时,根据传入的参数我们可以分析strongExtraCount
为0
,unownedCount
为1
;根据其实现我们可以看到,是将strongExtraCount
(强引用计数)和unownedCount
(无主引用计数)通过位移的方式,存储在了64
位信息中; 在源码中我们可以分析如下:
# define shiftAfterField(name) (name##Shift + name##BitCount) static const size_t PureSwiftDeallocShift = 0; static const size_t PureSwiftDeallocBitCount = 1; static const size_t StrongExtraRefCountShift = shiftAfterField(IsDeiniting); static const size_t PureSwiftDeallocShift = 0; static const size_t UnownedRefCountShift = shiftAfterField(PureSwiftDealloc); static const size_t IsDeinitingShift = shiftAfterField(UnownedRefCount); static const size_t IsDeinitingBitCount = 1; static const size_t UnownedRefCountShift = shiftAfterField(PureSwiftDealloc); static const size_t UnownedRefCountBitCount = 31; // 结果分析 StrongExtraRefCountShift = shiftAfterField(IsDeiniting) = IsDeinitingShift + IsDeinitingBitCount = shiftAfterField(UnownedRefCount) + 1 = UnownedRefCountShift + UnownedRefCountBitCount + 1 = shiftAfterField(PureSwiftDealloc) + 31 + 1 = PureSwiftDeallocShift + PureSwiftDeallocBitCount + 31 + 1 = 0 + 1 + 31 + 1 = 33 复制代码
通过上述计算,我们可以分析如下:
bits( (0 << 33) | (1 << 0) | (1 << 1))
bits( 0 | 1 | 2)
bit(3)
与我们最终打印的0x3
相匹配;
64位
存储的信息如下:
第
0
位:标识是否是永久的
第1-31
位:存储无主引用
第32
位:标识当前类是否正在析构
第33-62
位:标识强引用
第63
位:是否使用SlowRC
作者:每天写点代码
链接:https://juejin.cn/post/7063026344656896030