[Swift初学]系列之二:枚举原始值和关联值的存储差异

650 阅读4分钟

导读

[Swift初学]系列之一中,介绍了枚举使用原始值和关联值时,存储方式是不同的,但是也只是说了结论,没有提供验证方法,这篇文章是枚举的进阶,验证二者的存储差异

预准备

在直接验证之前,需要先了解一些验证时会用到的知识,这里直接说结论,深入的学习放在后续的计划中

基础汇编

mov指令:

  • 1、寄存器和寄存器之间的传送
  • 2、立即数到寄存器的传送
  • 3、寄存器到内存单元之间的传送
  • 4、立即数到内存单元的传送

mov指令后面通常会跟一个表示操作字节大小的字符,q代表8字节,b代表1字节; 例如movq/movb

rip寄存器: 存储cpu预执行指令的内存地址,可以理解为当前指令的下一条指令的内存地址. LLDB断点读取寄存器的命令为 register read xxx

内存地址获取工具

这里使用小马哥开源的一个内存查看工具Mems

代码测试

原始值

测试原始值的存储方式

enum TestEnum : String {
    case test1
    case test2
}

var t = TestEnum.test2;
print(Mems.ptr(ofVal: &t));

/** 汇编断点时,执行的汇编为
 0x1000016a3 <+19>:  movb   $0x1, 0x5b4e(%rip)   
 0x1000016aa <+26>:  movl   $0x1, %ecx

上面说到rip存储的下一条指令的地址,这里是0x1000016aa,加上0x5b4e的偏移 = 0x1000071F8
指令含义为:将立即数1存储到0x1000071F8内存地址处,movb代表占用1字节
*/

在xcode中使用内存查看Debug->Debug workflow -> View Memory 可以查看输入的内存地址的值 将0x1000071F8输入,发现该地址存储内容确实是0x01,另外你可以自己测试当枚举对象为test1时,汇编指令以及内存存储值,这里不做测试,存储值应该是0x00

从另一个方法获取枚举值的占用空间,也验证了原始值无论是Int,String 都不是直接存储的成员值内容,而且只占用一个字节,特殊情况只有一个case成员值时,实际占空空间为零

MemoryLayout<TestEnum>.stride; // 1 
MemoryLayout<TestEnum>.size; // 1

关联值

关联值测试:

enum TestEnum {
    case test1(Int, Int, Int)
    case test2(Int, Int, Int)
}

var t = TestEnum.test1(10,11,12);
print(Mems.ptr(ofVal: &t)); // 通过工具获取的枚举地址为 0x00000001000071d8

/**
汇编指令 : 

 // 将10 存储 内存地址 0x5a4a + %rip,rip这里是0x10000178e,所以计算地址为:0x1000071D8
 0x100001783 <+19>:  movq   $0xa, 0x5a4a(%rip)

 0x10000178e <+30>:  movq   $0xb, 0x5a47(%rip)

 0x100001799 <+41>:  movq   $0xc, 0x5a44(%rip)

 0x1000017a4 <+52>:  movb   $0x0, 0x5a45(%rip)

内存空间存储值:
 0A 00 00 00 00 00 00 00
 0B 00 00 00 00 00 00 00
 0C 00 00 00 00 00 00 00
 00 00 00 00 00 00 00 00
 00 00 00 00 00 00 00 00
*/

通过汇编指令,可以看到是将10,11,12关联值存储到了内存空间,存储首地址为: 0x1000071D8. 而通过获工具打印的枚举对象的内存地址也是:0x1000071D8 可以证明 关联值是存储到枚举对象内存空间的,占用枚举对象的空间,并且是根据关联值占用空间来为枚举对象分配大小的(多个关联值的情况,是最大关联值占用空间) ; 最后一行指令其实是标识字段,和原始值一样的作用,标识是第几个case成员值.

这里你可以测试,当第二个关联值占用空间不同时,是如何存储的,结论是 使用相同的存储空间,占用多少就使用多少,其他不占用的空间都是0x00类似c++中的共用体概念

总结

  • 原始值和基本枚举类型一样,只占用一个字节,作用是区分成员值是第几个
  • 关联值是根据关联类型实际分配枚举内存大小的,关联的值是存储到枚举内存中的
  • 当枚举只有一个case时,虽然MemoryLayout显示分配了一个字节,实际占用字节为0,

最后

初学Swift,会以整理笔记的方式增加学习过程的乐趣,如果文中有知识性错误,欢迎各位多多指教