Swift(二十)-枚举关联值及其大小

790 阅读4分钟

「这是我参与2022首次更文挑战的第28天,活动详情查看:2022首次更文挑战

关联值

枚举的设计思路可以帮助我们将一些简单的同类数据进行整合,比如,我们需要表示一个模型,我们可以定义如下枚举:

image.png

我们枚举Shape,定义了圆形矩形,但是这种枚举的定义只适合简单数据类型的定义,而不同的形状可能需要不同的参数。比如,圆形需要圆心和半径,矩形需要中心点和宽高来确定。

Swift语言中,对枚举设置关联值就可以完成这样的需求。在定义枚举值的时候,我们可以设置参数列表,这个参数列表就称为枚举的关联值。比如如下代码:

image.png

对于枚举变量来说,如果给定了关联值,那么其就没有了原始值

在创建关联值枚举的时候,我们需要提供参数列表中所需要的参数。

image.png

关联值匹配

switch-case结构中,匹配到枚举之后,可以通过参数捕获的方式来获取枚举的关联值,代码如下:

image.png

在上述代码中,写了两种参数捕获的方式,let直接在枚举前,或者let关联值前;如果需要修改关联值,可以将let修改为var,如下:

image.png

枚举的大小

在内存中枚举值类型,其存储在上;接下来我们来分析一下,枚举占用空间的大小;

没有关联值的枚举大小

我们先来看一个简单的枚举,打印其大小,代码如下:

image.png

结果打印其大小为1字节,这是因为对于没有指定类型的枚举TestValue来说,其默认是UInt8类型,刚好占用1字节;而由于UInt8最多存储256,所以当枚举超过256个的时候,将会升级为UInt16,之后UInt32UInt64依次升级(正常开发过程中,我们的枚举也不可能有这么多);

枚举的隐式值已经硬编码进MachO文件中,我们只需要存储枚举值

我们可以通过查看其内存数据来验证一下:

image.png

通过打印我们发现,ab``c在内存中是连续存储的,并且其内存地址都相差1字节

只有一个关联值的枚举大小

那么,如果我们的枚举关联值,其大小是怎么计算的呢?我们看下边示例代码:

image.png

接下来,分别将Bool类型修改为Int类型进行打印:

image.png

结果打印为9,这是什么愿意呢?

在计算枚举类型大小时,系统会判断关联值的类型是否有额外的空间来存储枚举值的大小

Bool为例,Bool占用1个字节,也就是8位,但是Bool类型实际占用的空间只是这8位中的1位,会多出来7位 ,也就是128个,我们的枚举并没有那么多,所以最终只有1字节就可以存储下枚举;

Int为例,Int占用8个字节,也就是64位,没有多余的存储空间,需要额外的空间来存储其枚举值,所以最终是8+1=9个字节;但是需要注意的是,根据字节对齐原则,其步长此时为16

关联值类型不同

我们前边计算的枚举,它的关联值类型都相同,如果枚举的关联值类型不同呢?如下代码所示:

image.png

在此枚举中,虽然都只是一个关联值,但是却又Bool``IntString三种类型,这个时候我们需要比较三个关联值,找到占用空间最大的那个关联值类型,此处为String,占用16字节,所以最终大小为16 + 1 = 17

有多个关联值的枚举大小

那么,如果我们的枚举中,有多个关联值呢?我们来看下边示例代码:

image.png

枚举中有三个Int和一个Bool,最终打印结果是25,这是因为,3Int占用了24字节,Bool占用了1个字节中的1位,剩下的7位可以存放枚举值,所以最终结果是3 * 8 + 1 = 25

如果我们将其中一个枚举中的关联值交换顺序呢?如下所示:

image.png

最终结果却变成了33,这是为什么呢?其大小的计算我们可以这么理解:

纵观所有的case,找出关联值最多case,然后每一个case的关联值按照顺序逐个比较,找出占用空间最大的关联值:第一个关联值占用空间最大的为Int类型,是8字节,剩下的第二,第三,第四关联值依次如此比较,最终发现,占用空间最大的是4Int,那么最终枚举大小为4 * 8 + 1 = 33