Swift語法 Swift5 【04 - 枚舉】


  • 作者: Liwx
  • 郵箱: 1032282633@qq.com
  • 源碼: 需要源碼的同學, 可以在評論區(qū)留下您的郵箱

iOS Swift 語法 底層原理內(nèi)存管理分析 專題:【iOS Swift5語法】

00 - 匯編
01 - 基礎(chǔ)語法
02 - 流程控制
03 - 函數(shù)
04 - 枚舉
05 - 可選項
06 - 結(jié)構(gòu)體和類
07 - 閉包
08 - 屬性
09 - 方法
10 - 下標
11 - 繼承
12 - 初始化器init
13 - 可選項


目錄

  • 01-枚舉的基本用法
  • 02-關(guān)聯(lián)值(Associated Values)
  • 03-關(guān)聯(lián)值舉例
  • 04-原始值(Raw Values)
  • 05-隱式原始值(Implicitly Assigned Raw Values)
  • 06-遞歸枚舉(Recursive Enumeration)
  • 07-內(nèi)存布局(MemoryLayout)

01-枚舉的基本用法

// 寫法1
enum Direction {
    case north
    case south
    case east
    case west
}

// 寫法2
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")  // north
case .south:
    print("south")
case .east:
    print("east")
case .west:
    print("west")
}

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

  • 關(guān)聯(lián)值
    • 將枚舉的成員值其他類型的值關(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
  • 必要時let也可以改為var
enum Date {
    case digit(year: Int, month: Int, day: Int)  // 可使用標簽定義
    case string(String)  
}

var date = Date.digit(year: 2020, month: 1, day: 2)
date = .string("2020-01-03")
switch date {
case .digit(var year, let month, let day):  // year var
    print(year, month, day)
case let .string(value):
    print(value)
}  // 2020-01-03

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

enum Password {
    case number(Int, Int, Int, Int)
    case gesture(String)
}

var pwd = Password.number(3, 5, 7, 8)
pwd = .gesture("12369")

switch pwd {
case let .number(n1, n2, n3, n4):
    print("number is ", n1, n2, n3, n4)
case let .gesture(str):
    print("gesture is ", str)
}

04-原始值(Raw Values)

  • 原始值
    • 枚舉成員可以使用相同類型的默認值與之對應(yīng), 這個默認值叫做: 原始值
    • 注意: 原始值不占用枚舉變量的內(nèi)存
// PokerSuit的原始值類型為Character
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) // ??
// Grade的原始值類型為String
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

05-隱式原始值(Implicitly Assigned Raw Values)

  • 如果枚舉的原始值類型Int迎献、String, Swift會自動分配原始值

  • String類型自動分配原始值

// 寫法1
enum Direction : String {
    case north = "north"
    case south = "south"
    case east = "east"
    case west = "west"
}

// 寫法2 等價于寫法1
enum Direction : String {
    case north, south, east, west
}
print(Direction.north)  // north
print(Direction.north.rawValue) // north
  • Int類型自動分配原始值
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
  • Int類型自定義原始值
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

06-遞歸枚舉(Recursive Enumeration)

  • 遞歸枚舉定義關(guān)鍵字 indirect
    • 遞歸枚舉即枚舉成員存在遞歸調(diào)用的枚舉類型
  • 枚舉內(nèi)部成員添加indirect關(guān)鍵字, 在調(diào)用本身的內(nèi)部成員前加indirect
enum ArithExpr {
    case number(Int)
    indirect case sum(ArithExpr, ArithExpr)
    indirect case difference(ArithExpr, ArithExpr)
}
  • 枚舉外部添加indirect關(guān)鍵字
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 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)  // 枚舉遞歸調(diào)用
    case let .difference(left, right):
        return calculate(left) - calculate(right)  // 枚舉遞歸調(diào)用
    }
}

calculate(difference)   // 7

07-內(nèi)存布局(MemoryLayout)

  • 使用MemoryLayout獲取數(shù)據(jù)類型占用的內(nèi)存大小
enum Password {
    case num(Int, Int, Int, Int)
    case other
}

MemoryLayout<Password>.stride   // 40, 分配占用的空間大小
MemoryLayout<Password>.size     // 33, 實際用的空間大小
MemoryLayout<Password>.alignment// 8, 內(nèi)存對齊

var pwd = Password.num(9, 8, 6, 4)
pwd = .other
MemoryLayout.stride(ofValue: pwd)// 40, 分配占用的空間大小
MemoryLayout.size(ofValue: pwd) // 33, 實際用的空間大小
MemoryLayout.alignment(ofValue: pwd)// 8, 內(nèi)存對齊

  • iOS內(nèi)存布局: 小端

  • 查看內(nèi)存方式

    • 查看內(nèi)存方式1: 底部終端左側(cè)窗口,選擇要查看的變量/常量,右鍵菜單-View Memory of "變量/常量名"
    • 查看內(nèi)存方式2: Debug - DebugWorkflow - View Memory, 快捷鍵control+option+commond+shift+M 放開后再按enter
    • 使用Mems工具打印內(nèi)存地址 GitHub鏈接

  • 簡單查看常量/變量內(nèi)存布局
var a = 10
print(a)    // 這邊打斷點觀察
// Int型變量a內(nèi)存分配8字節(jié)
// 0A 00 00 00 00 00 00 00
image.png

  • 查看枚舉內(nèi)存布局
enum TestEnum {
    case test1, test2, test3
}

var t = TestEnum.test1  // 內(nèi)存: 00
t = .test2  // 內(nèi)存: 01
t = .test3  // 內(nèi)存: 02

print(Mems.ptr(ofVal: &t))  // 內(nèi)存: 02

print(MemoryLayout<TestEnum>.size)  // 這邊打斷點觀察  1
print(MemoryLayout<TestEnum>.stride)    // 1
print(MemoryLayout<TestEnum>.alignment) // 1
image.png

  • 枚舉原始值內(nèi)存查看
    • 枚舉類型后面冒號 : 后面的類型表示原始值類型
    • 原始值不影響枚舉變量內(nèi)存存儲
enum TestEnum1 : Int {  // 原始值為Int
    case test1 = 1, test2 = 2, test3 = 3
}

var t1 = TestEnum1.test1    // 內(nèi)存: 00
print(Mems.ptr(ofVal: &t1))
t1 = .test2 // 01
t1 = .test3 // 02
// - 原始值不影響枚舉變量內(nèi)存存儲

print(MemoryLayout<TestEnum1>.size)         // 1 斷點觀察
print(MemoryLayout<TestEnum1>.stride)       // 1
print(MemoryLayout<TestEnum1>.alignment)    // 1
image.png

  • 枚舉關(guān)聯(lián)值內(nèi)存查看
    • 1個字節(jié)存儲成員值(索引值)
    • N個字節(jié)存儲關(guān)聯(lián)值(N: 占用內(nèi)存最大的關(guān)聯(lián)值),任何一個case的關(guān)聯(lián)值都共用這N個字節(jié)
enum TestEnum2 {
    case test1(Int, Int, Int)
    case test2(Int, Int)
    case test3(Int)
    case test4(Bool)
    case test5
}

print(MemoryLayout<TestEnum2>.size)         // 25
print(MemoryLayout<TestEnum2>.stride)       // 32
print(MemoryLayout<TestEnum2>.alignment)    // 8

var t2 = TestEnum2.test1(1, 2, 3)
print(Mems.ptr(ofVal: &t2))
// 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 00 00 00 00 00 00 00// 成員值,類似索引值

// Mems.memStr內(nèi)存里面的內(nèi)容
print(Mems.memStr(ofVal: &t2))
// 0x0000000000000001 0x0000000000000002 0x0000000000000003 0x0000000000000000

print(Mems.memStr(ofVal: &t2, alignment: .one))
// 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x02 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x03 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00

t2 = .test2(4, 5)
// 04 00 00 00 00 00 00 00
// 05 00 00 00 00 00 00 00
// 00 00 00 00 00 00 00 00
// 01 00 00 00 00 00 00 00// 成員值,類似索引值

t2 = .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
// 02 00 00 00 00 00 00 00// 成員值,類似索引值

t2 = .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
// 03 00 00 00 00 00 00 00// 成員值,類似索引值

t2 = .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
// 04 00 00 00 00 00 00 00// 成員值,類似索引值

print(t2)

  • 如果枚舉只有一個成員值, 實際沒有用到內(nèi)存
enum TestEnum3 {
    case test
}

var t3 = TestEnum3.test
print(Mems.ptr(ofVal: &t3)) // 0x0000000000000001
print(t3)

print(MemoryLayout<TestEnum3>.size)         // 0 實際沒有用到內(nèi)存
print(MemoryLayout<TestEnum3>.stride)       // 1
print(MemoryLayout<TestEnum3>.alignment)    // 1

  • 枚舉只有一個關(guān)聯(lián)值的成員值內(nèi)存查看
    • 無需1個字節(jié)存儲成員值
enum TestEnum4 {
    case test(Int)
}

var t4 = TestEnum4.test(10)
print(Mems.ptr(ofVal: &t4))
print(t4)

print(MemoryLayout<TestEnum4>.size)         // 8
print(MemoryLayout<TestEnum4>.stride)       // 8
print(MemoryLayout<TestEnum4>.alignment)    // 8

  • 枚舉的switch語句底層是如何實現(xiàn)的?
  • 通過枚舉的成員值(類似索引值)來判斷要執(zhí)行哪個case
enum TestEnum5 {
    case test1(Int, Int, Int)
    case test2(Int, Int)
    case test3(Int)
    case test4(Bool)
    case test5
}

// - 通過枚舉的成員值(類似索引值)來判斷要執(zhí)行哪個case
// - TestEnum5.test2(10, 20) 僅僅只是內(nèi)存賦值操作, 不存在函數(shù)調(diào)用
var t5 = TestEnum5.test2(10, 20)
switch t5 {
case let .test1(v1, v2, v3):
    print("test1", v1, v2, v3)
case let .test2(v1, v2):
    print("test2", v1, v2)
case let .test3(v1):
    print("test3", v1)
case let .test4(v1):
    print("test4", v1)
case let .test5:
    print("test5")
} // test2 10 20
image.png

iOS Swift 語法 底層原理內(nèi)存管理分析 專題:【iOS Swift5語法】

下一篇: 05 - 可選項
上一篇: 03 - 函數(shù)


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
禁止轉(zhuǎn)載偎捎,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者麸祷。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市跌穗,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌安皱,老刑警劉巖想虎,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異耸成,居然都是意外死亡报亩,警方通過查閱死者的電腦和手機浴鸿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來弦追,“玉大人岳链,你說我怎么就攤上這事【⒓” “怎么了掸哑?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長零远。 經(jīng)常有香客問我苗分,道長,這世上最難降的妖魔是什么牵辣? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任摔癣,我火速辦了婚禮,結(jié)果婚禮上纬向,老公的妹妹穿的比我還像新娘择浊。我一直安慰自己,他們只是感情好罢猪,可當我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布近她。 她就那樣靜靜地躺著叉瘩,像睡著了一般膳帕。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上薇缅,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天危彩,我揣著相機與錄音,去河邊找鬼泳桦。 笑死汤徽,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的灸撰。 我是一名探鬼主播谒府,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼浮毯!你這毒婦竟也來了完疫?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤债蓝,失蹤者是張志新(化名)和其女友劉穎壳鹤,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體饰迹,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡芳誓,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年余舶,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片锹淌。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡匿值,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出葛圃,到底是詐尸還是另有隱情千扔,我是刑警寧澤,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布库正,位于F島的核電站曲楚,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏褥符。R本人自食惡果不足惜龙誊,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望喷楣。 院中可真熱鬧趟大,春花似錦、人聲如沸铣焊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽曲伊。三九已至叽讳,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間坟募,已是汗流浹背岛蚤。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留懈糯,地道東北人涤妒。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像赚哗,于是被迫代替她去往敵國和親她紫。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,722評論 2 345