阅读 81

Swift(十五)-内存管理之引用计数

内存管理

Swift语言延续了和Objective-C语言一样的思路进行内存管理,都是采用引用计数的方式来管理实例的内存空间;

自动引用计数

自动引用计数Automatic Reference Counting也就是我们常说的ARC,其是Objective-C语言与Swift语言中解决内存管理问题的一种手段;在开发过程中,对类实例进行不当的引用将会造成内存泄漏,当内存泄漏积累到一定程度就会给应用程序带来灾难性的后果;

Swift中关于引用计数的操作

我们创建一个Person类如下:

class Person {     var age: Int = 20     var name: String = "zhangSan" } 复制代码

接下来,我们来打印分析一下Person的实例的内存指针:

image.png

我们在之前的分析中已经知道,其第二个8字节存储的是refCounts,也就是0x3;在源码HeapObject.h文件中,我们可以找到HeapObject结构体中关于refCounts的定义:

image.png

分析InlineRefCounts可以发现,其是一个接收InlineRefCountBits泛型参数的模板类RefCounts

typedef RefCounts<InlineRefCountBits> InlineRefCounts; 复制代码

image.png

根据RefCounts的定义我们发现,其实质上是在操作我们传递的泛型参数RefCountsBits,而RefCounts其实是对引用计数的包装,而引用计数的具体类型,取决于传递的参数类型InlineRefCountBits

我们继续分析InlineRefCountBits的定义:

typedef RefCountBitsT<RefCountIsInline> InlineRefCountBits; 复制代码

其也是一个模板函数,而RefCountIsInline其实就是true

enum RefCountInlinedness { RefCountNotInline = false, RefCountIsInline = true }; 复制代码

那么,我们可以知道,引用计数最终操作的类其实是RefCountBitsT

image.png

我们分析RefCountBitsT的过程中,发现只有一个bits属性,而该属性是由RefCountBitsIntType属性定义的;而根据Type的定义,我们发现Type其实是一个uint64_t类型,是一个64位的位域信息:

typedef uint64_t Type; 复制代码

至此,我们可以得到结论:

引用计数本质上是一个64位的位域信息,在这64位信息中存储了引用计数的相关信息;之所以这么做是为了将其抽象出来,提高代码复用率;

那么,问题来了,我们创建一个新的实例对象时,他的引用计数是多少呢?从源码中我们找到HeapObject的初始化方法:

image.png

HeapObject的初始化实现中,我们找到了refCounts的赋值操作:

image.png

refCounts赋值了Initialized,我们继续分析发现Initialized是一个枚举类型Initialized_t

image.png

而根据源码显示,一个新的实例被创建时,传入的是RefCountBits(0,1),而RefCountBits对应我们上边分析过的RefCountBitsT,接下里,我们在源码中找到RefCountBitsT的初始化:

image.png

此时,根据传入的参数我们可以分析strongExtraCount0unownedCount1;根据其实现我们可以看到,是将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位存储的信息如下:

image.png

0位:标识是否是永久的
1-31位:存储无主引用
32位:标识当前类是否正在析构
33-62位:标识强引用
63位:是否使用SlowRC


作者:每天写点代码
链接:https://juejin.cn/post/7063026344656896030


文章分类
代码人生
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐