04-枚举

枚举的基本用法

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 语句来匹配枚举值

switch dir {
case .north:
    print("north")
case .south:
    print("south")
case .east:
    print("east")
case .west:
    print("west")
}

遍历枚举情况(case)

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

enum Beverage: CaseIterable {
    case coffee, tea, juice
}
let numberOfChoices = Beverage.allCases.count
print("\(numberOfChoices) beverages available")
// Prints "3 beverages available"

for beverage in Beverage.allCases {
    print(beverage)
}
// coffee
// tea
// juice

关联值(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, 用户  : Int)  
    case string(String) 
} 

var date = Date.digit(year: 2011, month: 9, day: 10) 
date = .string("2011-09-10") 

switch date { 
    case .digit(let year, let month, let day):  
    print(year, month, day) 
    case let .string(value):  
    print(value) 
} 
  • 必要时let也可以改为var

关联值举例

1.png

原始值 (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) // ♣  

enum Grade : String {
  case perfect = "A"  
  case great = "B"  
  case good = "C"  
  case bad = "D" 
} 
print(Grade.perfect.rawValue) // A 
print(Grade.great.rawValue) // B 
print(Grade.good.rawValue) // C 
print(Grade.bad.rawValue) // D  

注意:原始值不占用枚举变量

原始值与关联值不同。原始值是当你第一次定义枚举的时候,它们用来预先填充的值,正如上面的三个 ASCII 码。特定枚举成员的原始值是始终相同的。关联值在你基于枚举成员的其中之一创建新的常量或变量时设定,并且在你每次这么做的时候这些关联值可以是不同的。

隐式原始值 (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, winter 
} 
print(Season.spring.rawValue) // 0 
print(Season.summer.rawValue) // 1 
print(Season.autumn.rawValue) // 2 
print(Season.winter.rawValue) // 3 

enum Season : Int {
  case spring = 1, summer, autumn = 4, winter 
} 
print(Season.spring.rawValue) // 1 
print(Season.summer.rawValue) // 2 
print(Season.autumn.rawValue) // 4 
print(Season.winter.rawValue) // 5 

递归枚举 (Recursive Enumeration)

  • 递归枚举是拥有另一个枚举作为枚举成员关联值的枚举。当编译器操作递归枚举时必须插入间接寻址层。你可以在声明枚举成员之前使用 indirect关键字来明确它是递归的。
 indirect enum ArithExpr {
     case number(Int)
     case sum(ArithExpr, ArithExpr)
     case difference(ArithExpr, ArithExpr)  
 }  

 enum ArithExpr {
     case number(Int)
     indirect case sum(ArithExpr, ArithExpr) 
     indirect 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 difference = ArithExpr.difference(sum, two) 

 func calculate(_ expr: ArithExpr) -> Int {
  switch expr {
    case let .number(value):  
        return value  
    case let .sum(left, right):
         return calculate(left) + calculate(right)  
    case let .difference(left, right):
         return calculate(left) - calculate(right)  
   } 
 }  
 calculate(difference)  
image.png

MemoryLayout

  • 可以使用MemoryLayout获取数据类型占用的内存大小

  • 先看一下int占用多少个字节 (c语言中用sizeof,swift用MemoryLayout)

var age = 10
MemoryLayout<Int>.size // 8  和平台有关系 64位 Int 等价 Int64
  • 分析一下枚举到底占用多少内存
enum Password {
  case number(Int, Int, Int, Int)  
  case other  
}  

MemoryLayout<Password>.stride // 40, 分配占用的空间大小 
MemoryLayout<Password>.size // 33, 实际用到的空间大小 
MemoryLayout<Password>.alignment // 8, 对齐参数 

var pwd = Password.number(9, 8, 6, 4)  // 至少占用32 
pwd = .other                           // other存储到pwd中的时候,不会再动态改变pwd的内存

MemoryLayout.stride(ofValue: pwd) // 40 
MemoryLayout.size(ofValue: pwd) // 33 (32 + 1)
MemoryLayout.alignment(ofValue: pwd) // 8 
  • 分析一下原始值占用多少内存
enum Season : Int {
  case spring, summer, autumn, winter
}

原始值不会存储到内存中,固定死的,一个字节就够用了

enum Season : Int { // 原始值是int类型
  // 0 1 2 3 
  case spring = 1, summer, autumn, winter
}

var s = Season.spring // 0
var s1 = Season.spring // 0
var s2 = Season.spring // 0 
image.png

关联值的话,允许自己传值, 要存储到枚举变量内存里面

enum Password {
  case number(Int, Int, Int, Int)
  case other
}

var pwd1 = Password.number(22, 55, 789, 2030)
var pwd2 = Password.number(9, 8, 6, 4)
var pwd3 = Password.number(111, 222, 100, 40200)

MemoryLayout<Password>.stride
MemoryLayout<Password>.size
MemoryLayout<Password>.alignment
image.png

思考下面枚举变量的内存布局

enum TestEnum {
  case test1, test2, test3 
} 
var t = TestEnum.test1 
t = .test2 
t = .test3 
image.png
enum TestEnum : Int {
  case test1 = 1, test2 = 2, test3 = 3
} 
var t = TestEnum.test1 
t = .test2 
t = .test3 
image.png
enum TestEnum {
  case test 
} 

var t = TestEnum.test 
// 就一个case,不需要分配就是它自己
image.png
enum TestEnum {
  case test(Int) 
} 
var t = TestEnum.test(10) 
image.png
enum TestEnum {
  case test1(Int, Int, Int)  
  case test2(Int, Int)  
  case test3(Int)  
  case test4(Bool)  
  case test5 
} 

var e = TestEnum.test1(1, 2, 3) 

// 小端模式:高高低低 (01 00 00 00 00 00 00 00 00 -> 00 00 00 00 00 00 00 00 01)
// 01 00 00 00 00 00 00 00 00 
// 02 00 00 00 00 00 00 00 00
// 03 00 00 00 00 00 00 00 00
// 00  
// 00 00 00 00 00 00 00 00   

e = .test2(4, 5)   

// 04 00 00 00 00 00 00 00 00 
// 05 00 00 00 00 00 00 00 00
// 00 00 00 00 00 00 00 00 00
// 01 
// 00 00 00 00 00 00 00 00 

e = .test3(6)
// 推测                   
// 06 00 00 00 00 00 00 00 00 
// 00 00 00 00 00 00 00 00 00
// 00 00 00 00 00 00 00 00 00
// 02 
// 00 00 00 00 00 00 00 00 

e = .test4(true)                
// 推测                   
// 01 00 00 00 00 00 00 00 00 
// 00 00 00 00 00 00 00 00 00
// 00 00 00 00 00 00 00 00 00
// 03 
// 00 00 00 00 00 00 00 00 

e = .test5      
// 00 00 00 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 
// 00 00 00 00 00 00 00 00    

结论: 
1个字节存储成员值
N个字节存储关联值(N取占用内存最大的关联值,任何一个case的关联值都公用这个N个字节)
image.png

窥探内存

image.png
  • 打印当前e,Debug -> Debug Workflow -> View Memory 输入Mems打印出来的地址
image.png

进一步观察下面枚举的内存布局

  enum TestEnum {
    case test0  
    case test1  
    case test2  
    case test4(Int)  
    case test5(Int, Int)  
    case test6(Int, Int, Int, Bool)  
  } 
image.png
enum TestEnum {
  case test0  
  case test1  
  case test2  
  case test4(Int)  
  case test5(Int, Int)  
  case test6(Int, Bool, Int)  
}                                                
image.png
image.png
enum TestEnum {  
    case test0  
    case test1  
    case test2  
    case test4(Int)  
    case test5(Int, Int)  
    case test6(Int, Int, Bool, Int)  
} 
image.png

它们的switch语句底层又是如何实现的?

根据成员值那个字节来判断属于哪个枚举,然后进行操作。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,874评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,151评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,270评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,137评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,116评论 5 370
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,935评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,261评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,895评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,342评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,854评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,978评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,609评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,181评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,182评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,402评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,376评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,677评论 2 344

推荐阅读更多精彩内容