枚举及内存布局(学习笔记)
环境Xcode 11.0 beta4 swift 5.1
枚举的基本用法
enum Direction { case north case south case east case west } enum Direction { case north, south, east, west } // 以上两种写法是等价 var dir = Direction.west dir = Direction.east dir = .north print(dir) // north switch dir { case .north: print("north") case .south: print("south") case .east: print("east") case .west: print("west") }
- 关联值(Associated Values)
- 有时将枚举的成员值跟其他类型的值关联存储在一起会非常有用
enum Score { case points(Int) case grade(Character) } var score = Score.points(96) score = .grade("A") switch score { case let .points(i): print(i, "points") case let .grade(i): print("grade", i); } // grade A
enum Date { case digit(year: Int, month: Int, day: Int) case string(String) } var date = Date.digit(year: 2011, month: 10, day: 7) date = .string("2011-10-7") switch date { case .digit(let year, let month, var day): print(year, month, day) case let .string(value): print(value) } // year month day 可以用 `let` 或者 `var` 修饰
- 原始值(Raw Values)
- 枚举成员可以使用相同类型的默认值预先对应,这个默认值叫做原始值, 原始值不占用枚举变量的内存
enum PokerSuit : Character { case spade = "♠️" case heart = "♥️" case diamond = "♦️" case club = "♣️" } var suit = PokerSuit.spade print(suit) // spade print(suit.rawValue) // ♠️ print(PokerSuit.club.rawValue) // ♣️
- 隐式原始值(Implicitly Assigned Raw Values)
- 如果枚举的原始值类型是Int、 String, Swift会自动分配原始值
enum Direction : String { case north = "north" case south = "south" case east = "east" case west = "west" } // 上面的枚举变量的原始值等价下面 enum Direction : String { case north, south, east, west } print(Direction.north) // north print(Direction.north.rawValue) // north enum Season : Int { case spring, summer, autumn = 4, winter } print(Season.spring.rawValue) // 0 print(Season.summer.rawValue) // 1 print(Season.autumn.rawValue) // 4 print(Season.winter.rawValue) // 5
- 递归枚举(Recursive Enumeration)
- 使用关键字
indirect
, 可以用在enum
前面,也可以用在枚举成员变量前面(case
前面)
indirect enum ArithExpr { case number(Int) case sum(ArithExpr, ArithExpr) case difference(ArithExpr, ArithExpr) } let five = ArithExpr.number(5) let four = ArithExpr.number(4) let two = ArithExpr.number(2) let sum = ArithExpr.sum(five, four) let diff = ArithExpr.difference(sum, two) func cal(_ expr: ArithExpr) -> Int { switch expr { case let .number(value): return value case let .sum(left, right): return cal(left) + cal(right) case let .difference(left, right) return cal(left) - cal(right) } } cal(difference) // 7
- 使用关键字
枚举的内存布局
enum TestEnum { case test0(Int, Int, Int) case test1(Int, Int) case test2(Int) case test3(Bool) case test4 } print(MemoryLayout<TestEnum>.size) // 25 实际用到的内存 print(MemoryLayout<TestEnum>.stride) // 32 申请分配的内存 print(MemoryLayout<TestEnum>.alignment) // 8 内存对其参数 // 一共申请32个字节,实际用到25个字节,实际要用到的内存要按关联值占用到的最大的内存,在本例中是test0最大,关联3个Int类型,在64位系统中每个Int占用8个字节 // 因此要用24个字节,另外要一个字节即第25个字节用来标识枚举成员值,后面的7个字节用来内存补齐,以满足内存对齐 // 小端模式 // 01 00 00 00 00 00 00 00 // 02 00 00 00 00 00 00 00 // 03 00 00 00 00 00 00 00 // 00 (这一个字节标识枚举的成员值,0代表test0) // 00 00 00 00 00 00 00 var t = TestEnum.test0(1, 2, 3) // 小端模式 // 04 00 00 00 00 00 00 00 // 04 00 00 00 00 00 00 00 // 00 00 00 00 00 00 00 00 // 01 (这一个字节标识枚举的成员值,1代表test1) // 00 00 00 00 00 00 00 t = .test1(4, 5) // 小端模式 // 06 00 00 00 00 00 00 00 // 00 00 00 00 00 00 00 00 // 00 00 00 00 00 00 00 00 // 02 (这一个字节标识枚举的成员值,2代表test2) // 00 00 00 00 00 00 00 t = .test2(6) // 小端模式 // 01 00 00 00 00 00 00 00 // 00 00 00 00 00 00 00 00 // 00 00 00 00 00 00 00 00 // 03 (这一个字节标识枚举的成员值,3代表test3) // 00 00 00 00 00 00 00 t = .test3(true) // 小端模式 // 00 00 00 00 00 00 00 00 // 00 00 00 00 00 00 00 00 // 00 00 00 00 00 00 00 00 // 04 (这一个字节标识枚举的成员值,4代表test4) // 00 00 00 00 00 00 00 t = .test4