美文网首页iOS Swift5 语法
Swift语法 Swift5 【04 - 枚举】

Swift语法 Swift5 【04 - 枚举】

作者: Liwx | 来源:发表于2020-05-08 17:18 被阅读0次

    • 作者: Liwx
    • 邮箱: 1032282633@qq.com
    • 源码: 需要源码的同学, 可以在评论区留下您的邮箱

    iOS Swift 语法 底层原理内存管理分析 专题:【iOS Swift5语法】

    00 - 汇编
    01 - 基础语法
    02 - 流程控制
    03 - 函数
    04 - 枚举
    05 - 可选项
    06 - 结构体和类
    07 - 闭包
    08 - 属性
    09 - 方法
    10 - 下标
    11 - 继承
    12 - 初始化器init
    13 - 可选项


    目录

    • 01-枚举的基本用法
    • 02-关联值(Associated Values)
    • 03-关联值举例
    • 04-原始值(Raw Values)
    • 05-隐式原始值(Implicitly Assigned Raw Values)
    • 06-递归枚举(Recursive Enumeration)
    • 07-内存布局(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-关联值(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
    
    • 必要时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-关联值举例

    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)

    • 原始值
      • 枚举成员可以使用相同类型的默认值与之对应, 这个默认值叫做: 原始值
      • 注意: 原始值不占用枚举变量的内存
    // 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会自动分配原始值

    • 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)

    • 递归枚举定义关键字 indirect
      • 递归枚举即枚举成员存在递归调用的枚举类型
    • 枚举内部成员添加indirect关键字, 在调用本身的内部成员前加indirect
    enum ArithExpr {
        case number(Int)
        indirect case sum(ArithExpr, ArithExpr)
        indirect case difference(ArithExpr, ArithExpr)
    }
    
    • 枚举外部添加indirect关键字
    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)  // 枚举递归调用
        case let .difference(left, right):
            return calculate(left) - calculate(right)  // 枚举递归调用
        }
    }
    
    calculate(difference)   // 7
    

    07-内存布局(MemoryLayout)

    • 使用MemoryLayout获取数据类型占用的内存大小
    enum Password {
        case num(Int, Int, Int, Int)
        case other
    }
    
    MemoryLayout<Password>.stride   // 40, 分配占用的空间大小
    MemoryLayout<Password>.size     // 33, 实际用的空间大小
    MemoryLayout<Password>.alignment// 8, 内存对齐
    
    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, 内存对齐
    

    • iOS内存布局: 小端

    • 查看内存方式

      • 查看内存方式1: 底部终端左侧窗口,选择要查看的变量/常量,右键菜单-View Memory of "变量/常量名"
      • 查看内存方式2: Debug - DebugWorkflow - View Memory, 快捷键control+option+commond+shift+M 放开后再按enter
      • 使用Mems工具打印内存地址 GitHub链接

    • 简单查看常量/变量内存布局
    var a = 10
    print(a)    // 这边打断点观察
    // Int型变量a内存分配8字节
    // 0A 00 00 00 00 00 00 00
    
    image.png
    • 查看枚举内存布局
    enum TestEnum {
        case test1, test2, test3
    }
    
    var t = TestEnum.test1  // 内存: 00
    t = .test2  // 内存: 01
    t = .test3  // 内存: 02
    
    print(Mems.ptr(ofVal: &t))  // 内存: 02
    
    print(MemoryLayout<TestEnum>.size)  // 这边打断点观察  1
    print(MemoryLayout<TestEnum>.stride)    // 1
    print(MemoryLayout<TestEnum>.alignment) // 1
    
    image.png
    • 枚举原始值内存查看
      • 枚举类型后面冒号 : 后面的类型表示原始值类型
      • 原始值不影响枚举变量内存存储
    enum TestEnum1 : Int {  // 原始值为Int
        case test1 = 1, test2 = 2, test3 = 3
    }
    
    var t1 = TestEnum1.test1    // 内存: 00
    print(Mems.ptr(ofVal: &t1))
    t1 = .test2 // 01
    t1 = .test3 // 02
    // - 原始值不影响枚举变量内存存储
    
    print(MemoryLayout<TestEnum1>.size)         // 1 断点观察
    print(MemoryLayout<TestEnum1>.stride)       // 1
    print(MemoryLayout<TestEnum1>.alignment)    // 1
    
    image.png
    • 枚举关联值内存查看
      • 1个字节存储成员值(索引值)
      • N个字节存储关联值(N: 占用内存最大的关联值),任何一个case的关联值都共用这N个字节
    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内存里面的内容
    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)
    

    • 如果枚举只有一个成员值, 实际没有用到内存
    enum TestEnum3 {
        case test
    }
    
    var t3 = TestEnum3.test
    print(Mems.ptr(ofVal: &t3)) // 0x0000000000000001
    print(t3)
    
    print(MemoryLayout<TestEnum3>.size)         // 0 实际没有用到内存
    print(MemoryLayout<TestEnum3>.stride)       // 1
    print(MemoryLayout<TestEnum3>.alignment)    // 1
    

    • 枚举只有一个关联值的成员值内存查看
      • 无需1个字节存储成员值
    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语句底层是如何实现的?
    • 通过枚举的成员值(类似索引值)来判断要执行哪个case
    enum TestEnum5 {
        case test1(Int, Int, Int)
        case test2(Int, Int)
        case test3(Int)
        case test4(Bool)
        case test5
    }
    
    // - 通过枚举的成员值(类似索引值)来判断要执行哪个case
    // - TestEnum5.test2(10, 20) 仅仅只是内存赋值操作, 不存在函数调用
    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 语法 底层原理内存管理分析 专题:【iOS Swift5语法】

    下一篇: 05 - 可选项
    上一篇: 03 - 函数


    相关文章

      网友评论

        本文标题:Swift语法 Swift5 【04 - 枚举】

        本文链接:https://www.haomeiwen.com/subject/alxqnhtx.html