04-枚舉

枚舉的基本用法

enum Direction {
    case north
    case south
    case east
    case west
}

// 多個成員值可以出現(xiàn)在同一行中挤悉,要用逗號隔開:
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 會暴露一個包含對應(yīng)枚舉類型所有情況的集合名為 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

關(guān)聯(lián)值(Associated Values)

  • 有時將枚舉的成員值跟其他類型的值關(guān)聯(lián)存儲在一起尚氛,會非常有用
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

關(guān)聯(lián)值舉例

1.png

原始值 (Raw Values)

  • 枚舉成員可以使用相同類型的默認值預(yù)先對應(yīng)诀诊,這個默認值叫做:原始值
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  

注意:原始值不占用枚舉變量

原始值與關(guān)聯(lián)值不同。原始值是當(dāng)你第一次定義枚舉的時候阅嘶,它們用來預(yù)先填充的值属瓣,正如上面的三個 ASCII 碼。特定枚舉成員的原始值是始終相同的讯柔。關(guān)聯(lián)值在你基于枚舉成員的其中之一創(chuàng)建新的常量或變量時設(shè)定抡蛙,并且在你每次這么做的時候這些關(guān)聯(lián)值可以是不同的。

隱式原始值 (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)

  • 遞歸枚舉是擁有另一個枚舉作為枚舉成員關(guān)聯(lián)值的枚舉捏卓。當(dāng)編譯器操作遞歸枚舉時必須插入間接尋址層极祸。你可以在聲明枚舉成員之前使用 indirect關(guān)鍵字來明確它是遞歸的慈格。
 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獲取數(shù)據(jù)類型占用的內(nèi)存大小

  • 先看一下int占用多少個字節(jié) (c語言中用sizeof,swift用MemoryLayout)

var age = 10
MemoryLayout<Int>.size // 8  和平臺有關(guān)系 64位 Int 等價 Int64
  • 分析一下枚舉到底占用多少內(nèi)存
enum Password {
  case number(Int, Int, Int, Int)  
  case other  
}  

MemoryLayout<Password>.stride // 40, 分配占用的空間大小 
MemoryLayout<Password>.size // 33, 實際用到的空間大小 
MemoryLayout<Password>.alignment // 8, 對齊參數(shù) 

var pwd = Password.number(9, 8, 6, 4)  // 至少占用32 
pwd = .other                           // other存儲到pwd中的時候遥金,不會再動態(tài)改變pwd的內(nèi)存

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

原始值不會存儲到內(nèi)存中浴捆,固定死的,一個字節(jié)就夠用了

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

關(guān)聯(lián)值的話稿械,允許自己傳值选泻, 要存儲到枚舉變量內(nèi)存里面

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

思考下面枚舉變量的內(nèi)存布局

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    

結(jié)論: 
1個字節(jié)存儲成員值
N個字節(jié)存儲關(guān)聯(lián)值(N取占用內(nèi)存最大的關(guān)聯(lián)值美莫,任何一個case的關(guān)聯(lián)值都公用這個N個字節(jié))
image.png

窺探內(nèi)存

image.png
  • 打印當(dāng)前e页眯,Debug -> Debug Workflow -> View Memory 輸入Mems打印出來的地址
image.png

進一步觀察下面枚舉的內(nèi)存布局

  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語句底層又是如何實現(xiàn)的?

根據(jù)成員值那個字節(jié)來判斷屬于哪個枚舉,然后進行操作厢呵。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末窝撵,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子襟铭,更是在濱河造成了極大的恐慌碌奉,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,013評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件寒砖,死亡現(xiàn)場離奇詭異赐劣,居然都是意外死亡,警方通過查閱死者的電腦和手機哩都,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,205評論 2 382
  • 文/潘曉璐 我一進店門魁兼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人漠嵌,你說我怎么就攤上這事璃赡。” “怎么了献雅?”我有些...
    開封第一講書人閱讀 152,370評論 0 342
  • 文/不壞的土叔 我叫張陵碉考,是天一觀的道長。 經(jīng)常有香客問我挺身,道長侯谁,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,168評論 1 278
  • 正文 為了忘掉前任章钾,我火速辦了婚禮墙贱,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘贱傀。我一直安慰自己惨撇,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,153評論 5 371
  • 文/花漫 我一把揭開白布府寒。 她就那樣靜靜地躺著魁衙,像睡著了一般报腔。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上剖淀,一...
    開封第一講書人閱讀 48,954評論 1 283
  • 那天纯蛾,我揣著相機與錄音,去河邊找鬼纵隔。 笑死翻诉,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的捌刮。 我是一名探鬼主播碰煌,決...
    沈念sama閱讀 38,271評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼绅作!你這毒婦竟也來了拄查?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,916評論 0 259
  • 序言:老撾萬榮一對情侶失蹤棚蓄,失蹤者是張志新(化名)和其女友劉穎堕扶,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體梭依,經(jīng)...
    沈念sama閱讀 43,382評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡稍算,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,877評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了役拴。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片糊探。...
    茶點故事閱讀 37,989評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖河闰,靈堂內(nèi)的尸體忽然破棺而出科平,到底是詐尸還是另有隱情,我是刑警寧澤姜性,帶...
    沈念sama閱讀 33,624評論 4 322
  • 正文 年R本政府宣布瞪慧,位于F島的核電站,受9級特大地震影響部念,放射性物質(zhì)發(fā)生泄漏弃酌。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,209評論 3 307
  • 文/蒙蒙 一儡炼、第九天 我趴在偏房一處隱蔽的房頂上張望妓湘。 院中可真熱鬧,春花似錦乌询、人聲如沸榜贴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,199評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽唬党。三九已至鹃共,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間初嘹,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,418評論 1 260
  • 我被黑心中介騙來泰國打工沮趣, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留屯烦,地道東北人。 一個月前我還...
    沈念sama閱讀 45,401評論 2 352
  • 正文 我出身青樓房铭,卻偏偏與公主長得像驻龟,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子缸匪,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,700評論 2 345

推薦閱讀更多精彩內(nèi)容