「这是我参与2022首次更文挑战的第8天,活动详情查看:2022首次更文挑战」。
- 本文主要介绍swift中枚举的相关知识
1.枚举的基本使用
1.1 oc中的枚举使用
回顾下我们在oc或者c中定义枚举一般的写法,其中NS_ENUM的第一个参数是类型,第二个参数是枚举的名称
///交通拥堵状态
typedef NS_ENUM(NSInteger, MATrafficStatus)
{
MATrafficStatusSmooth = 1, ///< 1 通畅
MATrafficStatusSlow, ///< 2 缓行
MATrafficStatusJam, ///< 3 阻塞
MATrafficStatusSeriousJam, ///< 4 严重阻塞
};
或者命名的方法在最后面,不指定类型,根据我们定义的类型。
typedef enum {
AFEncapsulationBoundaryPhase = 1,
AFHeaderPhase = 2,
AFBodyPhase = 3,
AFFinalBoundaryPhase = 4,
} AFHTTPBodyPartReadPhase;
如果没有设置枚举默认值,一般第一个枚举成员的默认值为整型0,后面依次递推
typedef enum:NSInteger {
MyEnumValueA,//默认0
MyEnumValueB,
MyEnumValueC,
} MyEnum;
1.2 swift中枚举的使用
enum week {
case MON
case TUE
case WED
case THU
case FRI
}
相当于
enum Weak{ case MON, TUE, WED, THU, FRI }
我们也可以指定类型比如Int
enum week:Int {
case MON = 1
case TUE
case WED
case THU
case FRI
}
String类型
enum week:String {
case MON = "周一"
case TUE = "周二"
case WED = "周三"
case THU = "周四"
case FRI = "周五"
}
我们如果不想写的话,可以指定默认的隐式值RawValue进行分配,我们当前的case类型的值就是这个类型。
修改的话就不使用默认值了
Int类型的默认值
修改后之前的会使用默认值,后面会根据我们传入的值进行累加。
2. 枚举的关联值
我们如果想用枚举表达一个复杂的类型,就需要使用关联值了,比如我们定义一个枚举位形状,里面有圆形,正方形,长方形等
enum Shape{
//case枚举值后括号内的就是关联值
case circle(radius: Double)
case rectangle(width: Int, height: Int)
case square(width:CGFloat)
}
let square = Shape.square(width: 20.0)
print(square)
当使用了关联值后,就没有RawValue了,主要是因为case可以用一组值来表示,而rawValue是单个的值
我们想要读取的话可以使用switch匹配模式进行判断读取你面的值,如果不想匹配所有的 case ,使用 defalut 关键字
switch square{
case .circle(radius: let radius):
print(radius)
case .rectangle(width: let width, height: let height):
print(width+height)
case .square(var width):
print(width)
width += 10
print(width)
}
我们要修改参数的话可以使用var进行定义,但是只会在闭包内进行改变,外部不变。因为本身就是一个值类型
除非我们重新赋值
3. 枚举的大小
3.1 No-payload enums
大家可以看到这种枚举类型类似我们在 C 语言中的枚举,当前类型默认是 Int 类型,那么 对于这一类的枚举在内存中是如何布局?以及在内存中占用的大小是多少那?这里我们就可以直 接使用 MemoryLayout 来测量一下当前枚举
打印发现枚举的size,stride都是1。在 Swift 中进行枚举布局的时候一直是尝试使用最少的空间来存储 enum ,对于当前的 case 数量来说, UInt8 能够表示 256 cases ,也就意味着如果一个默认枚举类型且没有关联值的 case 少于 256 ,当前枚举类型的大小 都是 1 字节。
通过上面的打印我们可以直观的看到,当前变量 a , b , c , d , e 五个变量存储的内容分别 是 00, 01, 02 ,03,04 这和我们上面说的布局理解是一致的。
3.2 single-payload enums
我们枚举的case 有单个负载时,大小怎么计算的
打印的结果还是1,很好理解,Bool值1字节,我们的枚举默认也是1字节所以当 前能表达 256 个 case的情况,对于 布尔类型来说,只需要使用低位的 0, 1 这两种情况,其 他剩余的空间就可以用来表示没有负载的 case 值。
- 我们换成
Int
Int类型在swift中是8字节,最大存储的是8字节,这个时候就需要开辟额外的空间来存储我们的case,因此是8+1 = 9;
3.3 mutil-payload enums
上面我们分析的是一个负载,当有多个负载呢
大小还是1字节,但是我们发现存储的内容发生了变化,那么00 41 80 81 c0代表什么意思呢?首先 bool 类型需要 1 字节,也就是8位。对于 bool 类型来说,我们存储的无非就是 0 或 1 ,只需要用到 1 位,所以剩余的 7 位这里我们都统称为 common spare bits,对于当前的case,我们可以把它放到common spare bits中,因此只用1字节即可。
我们看下00 41 80 81 c0中前面的0,4,8 叫做tag value 后面的 0, 1 这里我们就做tag index
当我们存在多个case的时候
我们分析还是和上面一样就是找到关联值的大小 取 对应枚举关联值最大的 ,所以: 8+1 = 9
enum的size = 最大关联值大小 + case(枚举值)大小 = 16 + 1 = 17,而stride由于8字节对齐,所以自动补齐到24
只有一个 case ,我们不需要用任何东⻄来去区分当前的 case ,所以当我 大小你会发现时 0。
4.总结
- 枚举我们通常用来表示一些
基础的样式,比如网络请求的状态,用户的状态等。通常我们会指定类型,没有的话默认是Int类型。我们可以指定枚举的值,不指定的话会有默认rawValue。String类型的枚举默认值就是我们定义的case;Int类型的则是枚举的下标index,我们指定了某一个case的Int值,则后面case的值自动累加。 - 枚举我们也可以使用
关联值来更好的表达我们想要的效果,比如形状的一些参数,我们通过switch来进行读取,不要每个都写可以使用defalut。 - 枚举的大小
默认是1字节,不指定类型的话是Int类型,可以存放256个case,超过的话Uint8——>UInt16->UInt32等扩容。枚举的大小是 =case中最大关联值+枚举值大小。对于关联值是Bool类型,存储的就是0/1因此可以用剩余的位置存储case. - 我们可以把枚举理解成一个
联合体,有些方面是很类似的。