「这是我参与2022首次更文挑战的第23天,活动详情查看:2022首次更文挑战」
内存管理
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