从零学习Swift 03:汇编分析枚举内存
今天我们用汇编分析一下枚举的内存布局.
一:普通枚举
enum TestEnum{
case test1,test2,test3,test4
}
var test = TestEnum.test1
print(MemoryLayout.size(ofValue: test)) //打印 1
print(MemoryLayout.stride(ofValue: test))//打印 1
print(MemoryLayout.alignment(ofValue: test))//打印 1
通过打印结果看到普通枚举类型的枚举变量只占用了一个字节,那么这一个字节里面存储着什么内容呢?我们直接从内存中看看这一个字节存储了什么,为了取得这个变量的内存地址,我们需要借助一个窥探Swift内存的小工具
结果如下:
![](https://img.haomeiwen.com/i2258226/d4deacee5ede5fa5.png)
![](https://img.haomeiwen.com/i2258226/5ae7c9e718d06e39.png)
从结果可以看出,普通枚举变量的内存地址中存储就是枚举的成员值:0,1,2,3
二:有原始值的枚举
代码如下:
//有原始值的枚举
enum TestEnum: Int{
case test1 = 1,test2 = 2,test3 = 3,test4 = 4
}
var test = TestEnum.test1
print(MemoryLayout.size(ofValue: test))
print(MemoryLayout.stride(ofValue: test))
print(MemoryLayout.alignment(ofValue: test))
print(Mems.ptr(ofVal: &test))
test = .test2
test = .test3
test = .test4
print(test)
结果如下:
![](https://img.haomeiwen.com/i2258226/765d0b17a3e5af69.png)
断点直接过到test3
:
![](https://img.haomeiwen.com/i2258226/a0b41f694e8d1947.png)
可以看到原始值和普通的枚举类型一样,枚举变量都只占用一个字节,存储枚举的成员值.
三:有关联值的枚举
//有关联值的枚举
enum TestEnum{
case test1(Int,Int,Int)
case test2(Int,Int)
case test3(Int)
case test4
}
var test = TestEnum.test1(1, 2, 3)
print(MemoryLayout.size(ofValue: test)) // 打印 25
print(MemoryLayout.stride(ofValue: test)) // 打印 32
print(MemoryLayout.alignment(ofValue: test)) // 打印 8
print(Mems.ptr(ofVal: &test))
test = .test2(4, 5)
test = .test3(6)
test = .test4
print(test)
从打印结果看到,系统为有关联值的枚举变量分配了32个字节,但是它实际上只用了25个字节.我们直接从从内存中看看这25个字节中存放着什么内容:
![](https://img.haomeiwen.com/i2258226/0a5e07df4af94957.png)
断点停在test3
,看看枚举值为test2
时,内存中的内容:
![](https://img.haomeiwen.com/i2258226/667f35a0cbb0550c.png)
test3
时:
![](https://img.haomeiwen.com/i2258226/c9dd94779431d84b.png)
test4
时:
![](https://img.haomeiwen.com/i2258226/334f144275069b87.png)
可以看到,关联值和原始值有很大不同,枚举的关联值是存储在枚举变量的内存中的.并且需要一个字节的额外空间来存储枚举的成员值(0,1,2,3,...)用来区分具体的成员;另外还需要N个字节来存储关联值,所有 case 下的关联值都共用这 N 个字节, N 就是占用最大内存的关联值.因为本实例代码中都是 Int 类型,而 Int 类型在 Swift 中占用 8 个字节,所以需要 24 个字节来存储关联值,然后再加上存储成员值的 1 个字节,实际占用 25 个字节.
四:只有一种 case 的特殊情况
![](https://img.haomeiwen.com/i2258226/482d9b43b2361f10.png)
如果只有一种case
,枚举变量就不会占用内存,虽然系统给这个枚举变量分配了1个字节的空间,但是它并没有使用.因为只有一种情况,根本不需要存储成员值来辨别具体是哪种case
.
关联值也是一样:
![](https://img.haomeiwen.com/i2258226/1e2a5bbffaf74180.png)
因为只有一种
case
,所以根本也不需要额外的一个字节存储成员值.
汇编分析
接下来我们使用汇编语言分析有关联值的枚举和 Switch 组合时的本质:
//汇编分析 枚举 switch
enum TestEnum{
case test1(Int,Int,Int)
case test2(Int,Int)
case test3(Int)
case test4
}
var e = TestEnum.test2(10, 20)
switch e {
case let .test1(v1, v2, v3):
print("test1:",v1,v2,v3)
case let .test2(v1, v2):
print("test2:",v1,v2)
case let .test3(v1):
print("test3:",v1)
case .test4:
print("test4")
}
断点打在var e = TestEnum.test2(10, 20)
位置,然后查看汇编代码如下:
-
第一步:给枚举变量
e
的内存空间赋值:
-
第二步:通过标识符找到具体的分支
- 第三步:执行具体分支代码
![](https://img.haomeiwen.com/i2258226/7d204dc850b60df0.png)
接上:
![](https://img.haomeiwen.com/i2258226/09a092d652c25ba8.png)