Swift进阶07:枚举enum

补充:添加脚本自动生成SIL

  • 通过target -> +,选择 other -> Aggregate,然后命名为HTScript
image
  • 选中HTScript,选择Build Phases -> 添加New Run Script Phase
image
  • Run Script中输入以下命令
swiftc -emit-sil ${SRCROOT}/HTEnumTest/main.swift | xcrun swift-demangle > ./main.sil && "/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code" main.sil
  • 选中HTScriptBuild,可以自动生成SIL文件(即 main.swift),并且vs code自动打开文件
    image

C中的枚举

在介绍swift中的枚举之前,首先我们来回顾下C中的枚举写法,如下所示

enum 枚举名{
    枚举值1,
    枚举值2,
    ......
};


enum Weak{
    MON, TUE, WED, THU, FRI, SAT, SUN
};


//如果没有设置枚举默认值,一般第一个枚举成员的默认值为整型0,后面依此类推
enum Weak{
    MON = 1, TUE, WED, THU, FRI, SAT, SUN
};


//表明创建了一个枚举,并声明了一个枚举变量weak
enum Weak{
    MON = 1, TUE, WED, THU, FRI, SAT, SUN
}weak;
//或者下面这种写法,省略枚举名称
enum{
    MON = 1, TUE, WED, THU, FRI, SAT, SUN
}weak;

Swift中的枚举

在swift中,枚举的创建方式如下所示,如果没有指定枚举值的类型,那么enum默认枚举值是整型的


enum Weak{
    case mon
    case tue
    case wed
    case thu
    case fri
    case sat
    case sun
}


//也可以直接一个case,然后使用逗号隔开
enum Weak{
    case mon, tue, wed, thu, fri, sat, sun
}


var w: Weak = .mon
  • 如果此时想创建一个枚举值是String类型的enum,可以通过指定enum的枚举值的类型来创建,其中枚举值和原始值rawValue的关系为case 枚举值 = rawValue原始值
/*
- =左边的值是枚举值,例如 mon
- =右边的值在swift中称为 RawValue(原始值),例如 "mon"
- 两者的关系为:case 枚举值 = rawValue原始值
*/
enum Weak: String{
    case mon = "mon"
    case tue = "tue"
    case wed = "wed"
    case thu = "thu"
    case fri = "fri"
    case sat = "sat"
    case sun = "sun"
}
  • 如果不想写枚举值后的字符串,也可以使用隐式RawValue分配,如下所示
//String类型
enum Weak: String {
    case mon, tue, wed = "wed", thu, fri, sat, sun
}
//Int类型,mon是从0开始依此类推,wed后是从10依此类推
enum Weak: Int {
    case mon, tue, wed = 10, thu, fri, sat, sun
}
image
  • 【注⚠️】:如果enum没有声明类型,是没有rawValue属性的
image

枚举的原始值

代码如下:

enum Weak: String {
    case mon, tue, wed, thu, fri, sat, sun
}

var value = Weak.mon.rawValue
print(value)

//---打印结果 mon

这里就有一个疑问,swift是如何做到打印 mon的?我们通过SIL文件分析

  • 首先查看SIL文件中的enum,底层多增加了一些东西
    • 1、给枚举值的类型,通过typealias取了一个别名RawValue
    • 2、默认添加了一个可选类型的init方法
    • 3、增加一个计算属性rawValue,用于获取枚举值的原始值
image
  • 查看SIL中的main方法,可以得知变量value是通过枚举值的rawValueget方法获取
image
  • 查看SIL文件rawValueget方法,主要有以下几步:
    • 1、接收一个枚举值,用于匹配对应的分支
    • 2、在对应分支创建对应的String
    • 3、返回对应的String
image

image

结论1:使用rawValue的本质是调用get方法
但是get方法中的String是从哪里来的呢?String存储在哪里?

  • 其实这些对应分支的字符串在编译时期就已经存储好了,即存放在Maach-O文件的__TEXT.cstring中,且是连续的内存空间,可以通过编译后查看Mach-O文件来验证
image

结论2rawValueget方法中的分支构建的字符串,主要是从Mach-O文件对应地址取出的字符串,然后再返回给value

总结

  • 使用rawValue的本质就是在底层调用get方法,即在get方法中从Mach-O对应地址中取出字符串并返回的操作

区分 case枚举值 & rawValue原始值

请问下面这段代码的打印结果是什么?

//输出 case枚举值
print(Weak.mon)
//输出 rawValue
print(Weak.mon.rawValue)

//---打印结果
mon
mon

虽然这两个输出的值从结果来看是没有什么区别的,虽然输出的都是mon,但并不是同一个东西

  • 第一个输出的case枚举值
  • 第二个是通过rawValue访问的rawValueget方法

如果我们像下面这种写法,编译器就会报错


image

枚举的init初始化

主要是探索枚举的init会在什么时候调用

  • 定义一个符号断点Weak.init
image

定义如下代码

enum Weak: String {
    case mon, tue, wed, thu, fri, sat, sun
}

var w = Weak.init(rawValue: "mon")
var sun = Weak(rawValue: "mon")

运行发现,这两种初始化方法都会触发调试断点

image

总结enuminit方法的调用是通过枚举.init(rawValue:)或者枚举(rawValue:)触发的

  • 我们再继续来分析init方法,来看下面这段代码的打印结果是什么?
var w = Weak.init(rawValue: "mon")
var sun = Weak(rawValue: "sunday")
print(w)
print(sun)

image

从运行结果可以看出,第一个输出的是可选值;第二个输出的是nil,表示没有找到对应的case枚举值。为什么会出现这样的情况呢?

  • 首先分析SIL文件中的Weak.init方法,主要有以下几步:
    • 1、在init方法中是将所有enum的字符串从Mach-O文件中取出,依次放入数组中
    • 2、放完后,然后调用_findStringSwitchCase方法进行匹配
image

image

image

其中 index_addr 表示获取当前数组中的第n个元素值的地址,然后再把构建好的字符串放到当前地址中

- `struct_extract` 表示`取出当前的Int值`,Int类型在系统中也是结构体
- `cond_br` 表示比较的表达式,即分支条件跳转
    - 如果匹配成功,则构建一个`.some的Optional`返回
    - 如果匹配不成功,则继续匹配,知道最后还是没有匹配上,则构建一个`.none的Optional`返回
  • swift-source中查找_findStringSwitchCase方法,接收两个参数,分别是 数组 + 需要匹配的String
    • 1、遍历数组,如果匹配则返回对应的index
    • 2、如果不匹配,则返回-1
@_semantics("findStringSwitchCase")
public // COMPILER_INTRINSIC
// 接收一个数组 + 需要匹配的string
func _findStringSwitchCase( 
  cases: [StaticString],
  string: String) -> Int {
// 遍历之前创建的字符串数组,如果匹配则返回对应的index
  for (idx, s) in cases.enumerated() {
    if String(_builtinStringLiteral: s.utf8Start._rawValue,
              utf8CodeUnitCount: s._utf8CodeUnitCount,
              isASCII: s.isASCII._value) == string {
      return idx
    }
  }
  // 如果不匹配,则返回-1
  return -1
}

枚举的遍历

  • 对于某些枚举来说,如果能有一个集合包含了枚举的所有情况就好了。你可以通过在枚举名字后面写 : CaseIterable 来允许枚举被遍历。Swift 会暴露一个包含对应枚举类型所有情况的集合名为 allCases

CaseIterable协议通常用于没有关联值的枚举,用来访问所有的枚举值,只需要对应的枚举遵守该协议即可,然后通过allCases获取所有枚举值,如下所示

//1、定义无关联值枚举,并遵守协议
enum Weak: String {
    case mon, tue, wed, thu, fri, sat, sun
}
extension Weak: CaseIterable {}

//2、通过for循环遍历
for oneCase in Weak.allCases {
    print(oneCase)
}

//3、通过函数式编程遍历
let allCase = Weak.allCases.map { $0.rawValue }.joined(separator: ",")
print(allCase)
//打印结果: mon,tue,wed,thu,fri,sat,sun

关联值

  • 如果希望用枚举表示复杂的含义,关联更多的信息,就需要使用关联值

例如,使用enum表达一个形状,其中有圆形、长方形等,圆形有半径,长方形有宽、高,我们可以通过下面具有关联值的enum来表示

//注:当使用了关联值后,就没有RawValue了,主要是因为case可以用一组值来表示,而rawValue是单个的值
enum Shape{
    //case枚举值后括号内的就是关联值,例如 radius
    case circle(radius: Double)
    case rectangle(width: Int, height: Int)
}

【注⚠️】具有关联值的枚举,就没有rawValue属性了,主要是因为一个case可以用一个或者多个值来表示,而rawValue只有单个的值

这一点我们也可以通过SIL文件 来验证

  • 首先查看SIL文件,发现此时的enum中既没有别名,也没有init方法、计算属性rawValue
image
  • 其中关联值中radiuswidthheight这些都是自定义的标签,也可以不写,如下所示,但并不推荐这种方式,因为可读性非常差
enum Shape{
    //case枚举值后括号内的就是关联值,例如 radius
    case circle(Double)
    case rectangle(Int, Int)
}
  • 关联值的枚举值的创建
//创建
var circle = Shape.circle(radius: 10.0)
//重新分配
circle = Shape.rectangle(width: 10, height: 10)

模式匹配

enum中的模式匹配其实就是匹配case枚举值

简单enum的模式匹配

【注】:swift中的enum模式匹配需要将所有情况都列举,或者使用default表示默认情况,否则会报错

enum Weak: String {
    case mon, tue, wed, thu, fri, sat, sun
}

var w: Weak?

switch w {
    case .mon:
        print(Weak.mon.rawValue)
    case .tue:
        print(Weak.tue.rawValue)
    default:
        print("unknow day")
}

//---打印结果 unknow day

查看其SIL文件,其内部是将nil放入w全局变量,然后匹配case,做对应的代码跳转

image

具有关联值enum的模式匹配

关联值的模式匹配主要有两种:

  • 1、通过switch匹配所有case
let shape = Shape.circle(radius: 10.0)
switch shape{
    //相当于将10.0赋值给了声明的radius常量
    case let .circle(radius):
        print("circle radius: \(radius)")
    case let .rectangle(width, height):
        print("rectangle width: \(width) height: \(height)")
}

//---打印结果: circle radius: 10.0
  • 2、也可以这么写,将关联值的参数使用let、var修饰
enum Shape{
    case circle(radius: Double)
    case rectangle(width: Int, height: Int)
}

let shape = Shape.circle(radius: 10)
switch shape{
    //做了Value-Binding,相当于将10.0赋值给了声明的radius常量
    case .circle(let radius):
        print("circle radius: \(radius)")
    case .rectangle(let width, var height):
        height += 1
        print("rectangle width: \(width) height: \(height)")
}

//---打印结果: circle radius: 10.0

然后查看SIL中的关联值的模式匹配,如下图所示

  • 1、首先构建一个关联值的元组
  • 2、根据当前case枚举值,匹配对应的case,并跳转
  • 3、取出元组中的值,将其赋值给匹配case中的参数
image
  • 通过if case匹配单个case,如下所示
enum Shape{
    case circle(radius: Double)
    case rectangle(width: Int, height: Int)
}

let shape = Shape.circle(radius: 10.0)
//匹配单个case
if case let Shape.circle(radius) = shape {
    print("circle radius: \(radius)")
}

//---打印结果: circle radius: 10.0
  • 如果我们只关心不同case的相同关联值(即关心不同case的某一个值),需要使用同一个参数,例如案例中的x,如果分别使用x、y, 编译器会报错
enum Shape{
    case circle(radius: Double)
    case rectangle(width: Double, height: Double)
    case square(width: Double, height: Double)
}
let shape = Shape.circle(radius: 10)
switch shape{
case let .circle(x), let .square(20, x):
    print(x)
default:
    break
}
  • 也可以使用通配符_(表示匹配一切)的方式,如下图所示
enum Shape{
    case circle(radius: Double)
    case rectangle(width: Double, height: Double)
    case square(width: Double, height: Double)
}
let shape = Shape.rectangle(width: 10, height:20)
switch shape{
case let .rectangle(x, _), let .square(_, x):
    print("x = \(x)")
default:
    break
}

枚举的嵌套

枚举的嵌套主要用于以下场景:

  • 1、【枚举嵌套枚举】一个复杂枚举是由一个或多个枚举组成
  • 2、【结构体嵌套枚举】enum是不对外公开的,即是私有的

枚举嵌套枚举

枚举嵌套枚举,实现方向的组合,如下图所示

enum CombineDirect{
    //枚举中嵌套的枚举
    enum BaseDirect{
        case up
        case down
        case left
        case right
    }
    //通过内部枚举组合的枚举值
    case leftUp(baseDIrect1: BaseDirect, baseDirect2: BaseDirect)
    case leftDown(baseDIrect1: BaseDirect, baseDirect2: BaseDirect)
    case rightUp(baseDIrect1: BaseDirect, baseDirect2: BaseDirect)
    case rightDown(baseDIrect1: BaseDirect, baseDirect2: BaseDirect)
}
//使用
let leftUp = CombineDirect.leftUp(baseDIrect1: CombineDirect.BaseDirect.left, baseDirect2: CombineDirect.BaseDirect.up)
print(leftUp)
image

结构体嵌套枚举

struct Skill {
    enum KeyType{
        case up
        case down
        case left
        case right
    }
    
    let key: KeyType
    
    func launchSkill(){
        switch key {
        case .left, .right:
            print("left, right")
        case .up, .down:
            print("up, down")
        }
    }
}

枚举属性方法

枚举中包含属性

enum中只能包含计算属性类型属性不能包含存储属性

enum Shape{
    case circle(radius: Double)
    case rectangle(width: Double, height: Double)
    
    //编译器报错:Enums must not contain stored properties 不能包含存储属性,因为enum本身是值类型
//    var radius: Double
    
    //计算属性 - 本质是方法(get、set方法)
    var with: Double {
        get {
            return 10.0
        }
    }
    //类型属性 - 是一个全局变量
    static let height = 20.0
}

为什么struct中可以放存储属性,而enum不可以?
主要是因为struct中可以包含存储属性是因为其大小就是存储属性的大小。而对enum来说就是不一样的,enum枚举的大小是取决于case的个数的,如果没有超过255,enum的大小就是1字节(8位)

枚举中包含方法

可以在enum中定义实例方法、static修饰的方法

enum Weak: Int {
    case mon, tue, wed, thu, fri, sat, sun
    
    mutating func nextDay() {
        self = Weak.init(rawValue: self.rawValue + 1) ?? Weak.mon
    }
}

//使用
var w = Weak.mon
w.nextDay()
print(w)

indirect关键字

如果我们想要表达的enum是一个复杂的关键数据结构时,可以通过indirect关键字来让当前的enum更简洁

//用枚举表示链表结构
enum List{
    case end
    //表示case使是引用来存储
    indirect case node(T, next: List)
}


//表示整个enum是用引用来存储
indirect enum List{
    case end
    case node(T, next: List)
}

为什么呢?

  • 因为enum值类型,也就意味着他们的大小在编译时期就确定了,那么这个过程中对于当前的enum的大小是不能确定的,从系统的角度来说,不知道需要给enum分配多大的空间,以下是官方文档的解释
You indicate that an enumeration case is recursive by writing indi rect before it, which tells the compiler to insert the necessary l ayer of indirection.
  • 打印enum的大小
image

如果传入的类型是String呢?


image

从结果发现,换成其他类型,其结果依旧是8,这是为什么呢?

下面来分析其内存结构,首先需要定义一个全局变量

enum List{
    case end
    indirect case node(T, next: List)
}

var node = List.node(10, next: List.end)

print(MemoryLayout.size(ofValue: node))
print(MemoryLayout.stride(ofValue: node))

通过lldb分析其内存


image

所以indirect关键字其实就是通知编译器,我当前的enum是递归的,大小是不确定的,需要分配一块堆区的内存空间,用来存放enum

  • 如果是end,此时存储的是case值,而case为nodeindirect修饰)时存储的是引用地址
image
  • 通过SIL来验证
image

swift和OC混编enum

在swift中,enum非常强大,可以添加方法、添加extension
而在OC中,enum仅仅只是一个整数值

如果想将swift中的enum暴露给OC使用:

  • @objc关键字标记enum
  • 当前enum应该是Int类型

OC调用Swift的enum


@objc enum Weak: Int {
    case mon, tue, wed, thu, fri, sat, sun
}


- (void)test {
    Weak mon = WeakMon;
}
  • 枚举必须用@objc修饰,必须是Int类型
image
image
image

Swift调用OC的enum

OC中的枚举会自动转换成swift中的enum


//会自动转换成swift的enum
NS_ENUM(NSInteger, OCENUM){
    Value1,
    Value2
};


//1、将OC头文件导入桥接文件
#import "HTOcTest.h"
//2、使用
let ocEnum = OCENUM.Value1
  • 如果OC中是使用typedef enum定义的,自动转换成swift就成了下面这样
typedef enum {
    Num1,
    Num2
}OCNum;


let typeEnum = OCNum.init(rawValue: 1)
print(typeEnum)

//*******打印结果*******
OCNum(rawValue: 0)

问题:OC如何访问swift中String类型的enum?

  • swift中的enum尽量声明成Int整型
  • 然后OC调用时,使用的是Int整型的
  • enum在声明一个变量/方法,用于返回固定的字符串,用于在swift中使用
@objc enum Weak: Int{
    case MON, TUE, WED
    
    var val: String?{
        switch self {
        case .MON:
            return "MON"
        case .TUE:
            return "TUE"
        case .WED:
            return "WED"
        default:
            return nil
        }
    }
}


Weak mon = WeakMON;


let weak = Weak.MON.val

枚举的大小

主要分析以下几种情况的大小:

  • 1、普通enum
  • 2、具有关联值的enum
  • 3、enum嵌套enum
  • 4、struct嵌套enum

1、普通enum大小分析

enum中不能包含存储属性,其根本在于enum的大小与Struct的计算方式是不一样的,这里我们将展开详细的分析

  • 首先,我们先来看看下面这段代码的打印结果是什么?
enum NoMean{
    case a
}
print(MemoryLayout.size)
print(MemoryLayout.stride)


0 //size大小是0
1 //表示访问下一个NoMean的case时,需要跨越1字节的步长
  • 如果此时增加一个 case b,此时的打印结果是什么?
enum NoMean{
    case a
    case b
}
print(MemoryLayout.size)
print(MemoryLayout.stride)


1 //size大小是1
1 //步长是1
  • 如果再增加多个呢?
enum NoMean{
    case a
    case b
    case c
    case d
}
print(MemoryLayout.size)
print(MemoryLayout.stride)


1
1
  • 如果继续增加 case的个数超过 256
image

从上面结果分析得出:

  • case默认是UInt8,即1字节(8位)最大可以存储256个case
  • 如果 case个数超过 256,会自动从 UInt8 -> UInt16 -> UInt32 -> UInt64 ...

LLDB分析

  • 分别定义4个全局变量tmp、tmp1、tmp2、tmp3
enum NoMean{
    case a
    case b
    case c
    case d
}

var tmp = NoMean.a
var tmp1 = NoMean.b
var tmp2 = NoMean.c
var tmp3 = NoMean.d

通过lldb查看内存情况如下,case都是1字节大小


image

2、具有关联值enum的大小分析

如果enum中有关联值,其大小又是多少呢?有如下代码,打印其size和stride

enum Shape{
    case circle(radius: Double)
    case rectangle(width: Double, height: Double)
}
print(MemoryLayout.size)
print(MemoryLayout.stride)


17 //size的大小是17
24 //stride的步长是24

说明从打印结果可以说明 enum中有关联值时,其内存大小取决于关联值的大小

  • enum有关联值时,关联值的大小 取 对应枚举关联值 最大的,例如circle中关联值大小是8,而rectangle中关联值大小是16,所以取16。所以enum的size = 最大关联值大小 + case(枚举值)大小 = 16 + 1 = 17,而stride由于8字节对齐,所以自动补齐到24
  • 定义一个全局变量,观察其内存
image

总结

  • 1、具有关联值的enum大小,取决于最大case的内存大小【枚举大小的本质】
  • 2、关联值枚举的大小 = 最大case的内存大小 + 1(case的大小)
  • 3、size 表示 实际大小
  • 4、stride 表示 对齐后的大小(内存空间中真实占用的大小)

3、enum嵌套enum的大小分析

请问下面这段代码的打印结果是什么?

enum CombineDirect{
    enum BaseDirect{
        case up, down, left, right
    }
    
    case leftUp(baseDirect1: BaseDirect, baseDirect2: BaseDirect)
    case rightUp(baseDirect1: BaseDirect, baseDirect2: BaseDirect)
    case leftDown(baseDirect1: BaseDirect, baseDirect2: BaseDirect)
    case rightDown(baseDirect1: BaseDirect, baseDirect2: BaseDirect)
}

print(MemoryLayout.size)
print(MemoryLayout.stride)


2 //size大小,enum有关联值取决于关联值的大小,每个case都有2个大小为1的enum,加上case枚举值 应该是 3,这里编译器做了优化,所以为2
2 //stride大小
image

总结

  • enum嵌套enum同样取决于最大case的关联值大小
  • 当嵌套enum的case只有2个时,case在内存中的存储是0、8
  • 当嵌套enum的case大于2,小于等于4时,case在内存中的存储是 0、4、8、c
  • 当嵌套enum的case大于4时,case在内存中的存储是从0、1、2...以此类推

4、结构体嵌套enum的大小分析

请问下面这段代码的打印结果是什么?

struct Skill {
    enum KeyType{
        case up
        case down
        case left
        case right
    }

    let key: KeyType

    func launchSkill(){
        switch key {
        case .left, .right:
            print("left, right")
        case .up, .down:
            print("up, down")
        }
    }
}
print(MemoryLayout.size)
print(MemoryLayout.stride)


1
1
  • 如果只嵌套了enum,没有声明变量,结构体的大小是多少呢?
struct Skill {
    enum KeyType{
        case up
        case down
        case left
        case right
    }
}
print(MemoryLayout.size)
print(MemoryLayout.stride)


0 //size的大小取决于成员变量,但是struct中目前没有属性
1
  • 如果不仅有枚举变量,还有其他属性,结构体的大小是多少呢?
struct Skill {
    enum KeyType{
        case up
        case down
        case left
        case right
    }

    let key: KeyType //1字节

    var height: UInt8 //1字节

    func launchSkill(){
        switch key {
        case .left, .right:
            print("left, right")
        case .up, .down:
            print("up, down")
        }
    }
}
print(MemoryLayout.size)
print(MemoryLayout.stride)


2
2
  • 如果在增加一个Int类型的属性呢?
struct Skill {
    enum KeyType{
        case up
        case down
        case left
        case right
    }
    
    var width: Int //8字节

    let key: KeyType //1字节

    var height: UInt8 //1字节

    func launchSkill(){
        switch key {
        case .left, .right:
            print("left, right")
        case .up, .down:
            print("up, down")
        }
    }
}
print(MemoryLayout.size)
print(MemoryLayout.stride)


10 //size大小(与OC中的结构体大小计算是一致的,min(m,n),其中m表示存储的位置,n表示属性的大小,要求是:m必须整除n)
16 //stride大小

总结

  • 1、如果结构体中没有其他属性,只有枚举变量,那么结构体的大小就是枚举的大小,即size为1
  • 2、如果结构体中嵌套了enum,但是没有声明变量,此时的size是0,stride是1
  • 3、如果结构体中还有其他属性,则按照OC中的结构体内存对齐三原则进行分析

内存对齐 & 字节对齐 区分

  • 内存对齐:iOS中是8字节对齐,苹果实际分配采用16字节对齐,这种只会在分配对象时出现
  • 字节对齐:存储属性的位置必须是偶地址,即OC内存对齐中的min(m,n),其中m表示存储的位置,n表示属性的大小,需要满足位置m整除n时,才能从该位置存放属性。简单来说,就是必须在自身的倍数位置开始
  • 外部调用对象时,对象是服从内存对齐
  • 单纯从结构上说,结构内部服从最大字节对齐

例如下面这个例子

struct Skill {
    var age: Int //8字节
    var height: UInt8 //1字节
    var width: UInt16 //2字节
}
print(MemoryLayout.size)
print(MemoryLayout.stride)


12
16
  • size为12的原因是:内存从0位置开始Int是占据0-7,UInt8占据8,下一个位置是9,但是UInt16是2字节对齐的要在它的倍数位置开始所以找下一个可以整除它的位置也就是UInt16占据10-11正好整个size在0-11,所以size为12
  • stride为16的原因:stride是实际分配的,必须是最大属性大小的整数倍,即8的倍数,所以是16

总结

  • 枚举说明:
    • 1、enum中使用rawValue的本质是调用get方法,即在get方法中从Mach-O对应地址中取出字符串并返回的操作
    • 2、enum中init方法的调用是通过枚举.init(rawValue:)或者枚举(rawValue:)触发的
    • 3、没有关联值的enum,如果希望获取所有枚举值,需要遵循CaseIterable协议,然后通过枚举名.allCase的方式获取
    • 4、case枚举值和rawValue原始值的关系:case 枚举值 = rawValue原始值
    • 5、enum的模式匹配方式,主要有两种:switch / if case
    • 6、enum可以嵌套enum,也可以在结构体中嵌套enum,表示该enum是struct私有的
    • 7、enum中还可以包含计算属性类型属性,但是不能包含存储属性
    • 8、enum中可以定义实例 + static修饰的方法
  • 枚举内存大小
    • 1、普通enum的内存大小一般是1字节,如果只有一个case,则为0,表示没有意义,如果case个数超过256,则枚举值的类型由UInt8->UInt16->UInt32...
    • 2、具有关联值的enum大小,取决于最大case的内存大小+case的大小(1字节)
    • 3、enum嵌套enum同样取决于最大case的关联值大小
    • 4、结构体嵌套enum,如果没有属性,则size为0,如果只有enum属性,size为1,如果还有其他属性,则按照OC中内存对齐原则进行计算

你可能感兴趣的:(Swift进阶07:枚举enum)