前言:
上篇文章主要介绍了Swift枚举
的基础用法
,用SIL文件
的方式分析了基础枚举
的编译结构和调用流程,这篇文章接着上篇文章的内容,继续以SIL的方式
分析其他枚举类型的组织结构
及枚举的内存大小
枚举的关联值分析
上篇文章中提到枚举的关联值是这样定义的:
//注:当使用了关联值后,就没有RawValue了,主要是因为case可以用一组值来表示,而rawValue是单个的值
enum Shape{
//case枚举值后括号内的就是关联值,例如 radius
case circle(radius: Double)
case rectangle(width: Int, height: Int)
}
关联值的SIL文件分析
生成sil文件,查看关联枚举的构成
关联枚举Switch匹配分析
关联枚举的匹配应用:
enum Shape{
//case枚举值后括号内的就是关联值,例如 radius
case circle(radius: Double)
case rectangle(width: Int, height: Int)
case square(width: Double, width: Double)
}
let shape = Shape.circle(radius: 10.0)
switch shape{
case let .circle(radious):
print("Circle radious:\(radious)")
case let .rectangle(width, height):
print("rectangle width:\(width),height\(height)")
}
生成sil文件,查看构成
查看case分支
其他关联枚举的使用:
-
通过
if case
匹配单个case,如下所示if case let Shape.circle(radius) = circle { print("circle radius: (radius)") }
-
如果我们只关心不同case的相同关联值(即关心不同case的某一个值),需要使用同一个参数,例如案例中的x,如果分别使用x、y, 编译器会报错
switch shape{ case let .circle(x), let .rectangle(20, x): print(x) default: break }
-
使用
通配符_(表示匹配一切)
的方式switch shape{ case let .rectangle(, x), let .square(, x): print("x = (x)") default: break }
switch shape{case let .rectangle(x, ), let .square(, x): print("x = (x)") default: break }
注意⚠️:
OC和Swift混编
只能调用swift中Int类型
的枚举
indirect关键字
递归枚举是一种枚举类型,它有一个或多个枚举成员使用该枚举类型的实例作为关联值。使用递归枚举时,编译器会插入一个间接层。你可以在枚举成员前加上indirect
来表示该成员可递归, 而且被 indirect
修饰符标记的枚举用例必须有一个关联值
//用枚举表示链表结构
enum List<T>{
case end
//表示case使是引用来存储
indirect case node(T, next: List<T>)
}
<!--也可以将indirect放在enum前-->
//表示整个enum是用引用来存储
indirect enum List<T>{
case end
case node(T, next: List<T>)
}
参考:《Swift语言中的关键字总结》
枚举的大小
上面已经整体分析了当前枚举的 rawValue 的存取操作,刚才在基础枚举分析源码的时候发现⼀个问题,那就是当前在 init
⽅法中分配了⼀个连续的字符串数组
,那么是不是意味着当前设置 rawValue 之后,我们当前枚举所占⽤的内存
就是这个连续数组的⼤⼩?
普通enum大小分析
- 案例1:
- 案例2:
- 案例3:
从以上案例结果分析,说明enum就是以1字节存储在内存中
的,这是为什么呢?我们来分析下
LLDB分析
所以当前枚举的步⻓
是 1 字节
,也就意味着如果在内存中连续存储 NoMean ,需要跨越⼀个字节的⻓度。⼀个字节也就是 8 位,最⼤可以表达的数字是多少? 255 对吧~,那这个时候可能⼜有⼀个问题,如果我有超过 255 个 Case 该怎么办?那么系统会⾃动默认⽤ UInt16 来
存储 Case ,依次类推~ ()
总结:
-
如果
enum中有原始值
,即rawValue,其大小取决于case的多少
,如果没有超过UInt8
即255,则就是1字节存储case
-
当只有一个case的情况下,
size
是0
,表示这个enum是没有分支,没有意义的 -
当有两个及以上case时,此时的enum是有意义的,如果没有超过255,则
case的步长是1字节
,如果超过,则UInt8->UInt16...,以此类推
关联enum的大小分析
从打印结果可以说明 enum中有关联值时,其内存大小取决于关联值的大小,``enum有关联值
时,关联值的大小 取 对应枚举关联值 最大的,例如circle中关联值大小是8,而rectangle中关联值大小是16,所以取16。所以enum的size = 最大关联值大小 + case(枚举值)大小
= 16 + 1 = 17,而stride
由于8字节对齐,所以自动补齐到24
总结:
-
有关联值的enum
大小,取决于最大case的内存大小
【枚举大小的本质】 -
关联值枚举的大小 = 最大case的内存大小 + 1(case的大小)
-
stride 表示 对齐后的大小(内存空间中真实占用的大小,8字节对齐)
结构体嵌套enum的大小分析
- 案例1
- 案例2
- 案例3:
总结:
-
如果结构体中嵌套了enum,但是没有声明变量,此时的size是0,stride是1
-
如果结构体中没有其他属性,只有枚举变量,那么结构体的大小就是枚举的大小,即size为1
-
如果结构体中还有其他属性,结构体的大小取决于元素的内存对齐三原则计算得出
总结:
-
enum的模式匹配方式,主要有两种:
switch / if case
-
enum中还可以
包含计算属性、类型属性
,但是不能包含存储属性
-
enum中可以定义
实例 + static修饰
的方法 -
具有关联值的枚举,可以成为
三无enum
,因为没有别名RawValue、init、计算属性rawValue -
普通enum的内存大小一般是
1字节
,如果只有一个case,则为0,表示没有意义,如果case个数超过255,则枚举值的类型由UInt8->UInt16->UInt32...
-
具有关联值的enum
大小,取决于最大case的内存大小
+case的大小(1字节) -
结构体嵌套enum,如果
没有属性,则size为0
,如果只有enum属性,size为1
,如果还有其他属性,则按照OC中内存对齐原则进行计算