从零学习Swift 03:汇编分析枚举内存

2020-04-10  本文已影响0人  小心韩国人

今天我们用汇编分析一下枚举的内存布局.

一:普通枚举
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内存的小工具
结果如下:

test1 test2

从结果可以看出,普通枚举变量的内存地址中存储就是枚举的成员值: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)

结果如下:


test1

断点直接过到test3:

test3

可以看到原始值和普通的枚举类型一样,枚举变量都只占用一个字节,存储枚举的成员值.

三:有关联值的枚举
//有关联值的枚举
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个字节中存放着什么内容:


test1

断点停在test3,看看枚举值为test2时,内存中的内容:

test2

test3时:

test3

test4时:

test4

可以看到,关联值和原始值有很大不同,枚举的关联值是存储在枚举变量的内存中的.并且需要一个字节的额外空间来存储枚举的成员值(0,1,2,3,...)用来区分具体的成员;另外还需要N个字节来存储关联值,所有 case 下的关联值都共用这 N 个字节, N 就是占用最大内存的关联值.因为本实例代码中都是 Int 类型,而 Int 类型在 Swift 中占用 8 个字节,所以需要 24 个字节来存储关联值,然后再加上存储成员值的 1 个字节,实际占用 25 个字节.

四:只有一种 case 的特殊情况

如果只有一种case,枚举变量就不会占用内存,虽然系统给这个枚举变量分配了1个字节的空间,但是它并没有使用.因为只有一种情况,根本不需要存储成员值来辨别具体是哪种case.

关联值也是一样:


因为只有一种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)位置,然后查看汇编代码如下:

接上:


上一篇 下一篇

猜你喜欢

热点阅读