Swift語(yǔ)法 Swift5 【04 - 枚舉】


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

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

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


目錄

  • 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)存儲(chǔ)在一起
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
  • 必要時(shí)let也可以改為var
enum Date {
    case digit(year: Int, month: Int, day: Int)  // 可使用標(biāo)簽定義
    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)

  • 原始值
    • 枚舉成員可以使用相同類型的默認(rèn)值與之對(duì)應(yīng), 這個(gè)默認(rèn)值叫做: 原始值
    • 注意: 原始值不占用枚舉變量的內(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)

  • 如果枚舉的原始值類型IntString, Swift會(huì)自動(dòng)分配原始值

  • String類型自動(dòng)分配原始值

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

// 寫法2 等價(jià)于寫法1
enum Direction : String {
    case north, south, east, west
}
print(Direction.north)  // north
print(Direction.north.rawValue) // north
  • Int類型自動(dòng)分配原始值
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, 實(shí)際用的空間大小
MemoryLayout<Password>.alignment// 8, 內(nèi)存對(duì)齊

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

  • 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鏈接

  • 簡(jiǎn)單查看常量/變量?jī)?nèi)存布局
var a = 10
print(a)    // 這邊打斷點(diǎn)觀察
// 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)  // 這邊打斷點(diǎn)觀察  1
print(MemoryLayout<TestEnum>.stride)    // 1
print(MemoryLayout<TestEnum>.alignment) // 1
image.png

  • 枚舉原始值內(nèi)存查看
    • 枚舉類型后面冒號(hào) : 后面的類型表示原始值類型
    • 原始值不影響枚舉變量內(nèi)存存儲(chǔ)
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
// - 原始值不影響枚舉變量?jī)?nèi)存存儲(chǔ)

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

  • 枚舉關(guān)聯(lián)值內(nèi)存查看
    • 1個(gè)字節(jié)存儲(chǔ)成員值(索引值)
    • N個(gè)字節(jié)存儲(chǔ)關(guān)聯(lián)值(N: 占用內(nèi)存最大的關(guān)聯(lián)值),任何一個(gè)case的關(guān)聯(lián)值都共用這N個(gè)字節(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)

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

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

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

  • 枚舉只有一個(gè)關(guān)聯(lián)值的成員值內(nèi)存查看
    • 無需1個(gè)字節(jié)存儲(chǔ)成員值
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語(yǔ)句底層是如何實(shí)現(xiàn)的?
  • 通過枚舉的成員值(類似索引值)來判斷要執(zhí)行哪個(gè)case
enum TestEnum5 {
    case test1(Int, Int, Int)
    case test2(Int, Int)
    case test3(Int)
    case test4(Bool)
    case test5
}

// - 通過枚舉的成員值(類似索引值)來判斷要執(zhí)行哪個(gè)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 語(yǔ)法 底層原理內(nèi)存管理分析 專題:【iOS Swift5語(yǔ)法】

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


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請(qǐng)通過簡(jiǎn)信或評(píng)論聯(lián)系作者。