swfit进阶-07-枚举

434 阅读1分钟

「这是我参与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类型的值就是这个类型。

image.png

修改的话就不使用默认值了

image.png

  • Int类型的默认值

image.png

修改后之前的会使用默认值,后面会根据我们传入的值进行累加

image.png

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单个的值

image.png

我们想要读取的话可以使用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进行定义,但是只会在闭包内进行改变,外部不变。因为本身就是一个值类型

image.png

除非我们重新赋值

image.png

3. 枚举的大小

3.1 No-payload enums

大家可以看到这种枚举类型类似我们在 C 语言中的枚举,当前类型默认是 Int 类型,那么 对于这一类的枚举在内存中是如何布局?以及在内存中占用的大小是多少那?这里我们就可以直 接使用 MemoryLayout 来测量一下当前枚举

image.png

打印发现枚举的size,stride都是1。在 Swift 中进行枚举布局的时候一直是尝试使用最少的空间来存储 enum ,对于当前的 case 数量来说, UInt8 能够表示 256 cases ,也就意味着如果一个默认枚举类型且没有关联值的 case 少于 256 ,当前枚举类型的大小 都是 1 字节。

image.png

通过上面的打印我们可以直观的看到,当前变量 a , b , c , d , e 五个变量存储的内容分别 是 00, 01, 02 ,03,04 这和我们上面说的布局理解是一致的。

3.2 single-payload enums

我们枚举的case 有单个负载时,大小怎么计算的

image.png

打印的结果还是1,很好理解,Bool1字节,我们的枚举默认也是1字节所以当 前能表达 256 个 case的情况,对于 布尔类型来说,只需要使用低位的 0, 1 这两种情况,其 他剩余的空间就可以用来表示没有负载的 case 值

  • 我们换成Int

image.png

Int类型在swift中是8字节,最大存储的是8字节,这个时候就需要开辟额外的空间来存储我们的case,因此是8+1 = 9

3.3 mutil-payload enums

上面我们分析的是一个负载,当有多个负载

image.png

大小还是1字节,但是我们发现存储的内容发生了变化,那么00 41 80 81 c0代表什么意思呢?首先 bool 类型需要 1 字节,也就是8位。对于 bool 类型来说,我们存储的无非就是 0 或 1 ,只需要用到 1 位,所以剩余的 7 位这里我们都统称为 common spare bits,对于当前的case,我们可以把它放到common spare bits中,因此只用1字节即可。

image.png

我们看下00 41 80 81 c0中前面的0,4,8 叫做tag value 后面的 0, 1 这里我们就做tag index

当我们存在多个case的时候

image.png

image.png

我们分析还是和上面一样就是找到关联值的大小 取 对应枚举关联值最大的 ,所以: 8+1 = 9

image.png

enum的size = 最大关联值大小 + case(枚举值)大小 = 16 + 1 = 17,而stride由于8字节对齐,所以自动补齐到24

只有一个 case ,我们不需要用任何东⻄来去区分当前的 case ,所以当我 大小你会发现时 0。

image.png

4.总结

  1. 枚举我们通常用来表示一些基础的样式,比如网络请求的状态,用户的状态等。通常我们会指定类型,没有的话默认是Int类型。我们可以指定枚举的值,不指定的话会有默认rawValueString类型的枚举默认值就是我们定义的caseInt类型的则是枚举的下标index,我们指定了某一个case的Int值,则后面case的值自动累加
  2. 枚举我们也可以使用关联值来更好的表达我们想要的效果,比如形状的一些参数,我们通过switch来进行读取,不要每个都写可以使用defalut
  3. 枚举的大小默认是1字节,不指定类型的话是Int类型,可以存放256个case,超过的话Uint8——>UInt16->UInt32等扩容。枚举的大小是 = case中最大关联值+枚举值大小。对于关联值是Bool类型,存储的就是0/1因此可以用剩余的位置存储case.
  4. 我们可以把枚举理解成一个联合体,有些方面是很类似的。