04-枚举

作者: 二斤寂寞 | 来源:发表于2023-04-05 13:59 被阅读0次

    枚举的基本用法

    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语句底层又是如何实现的?

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

    相关文章

      网友评论

          本文标题:04-枚举

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