Swift - 枚举

作者: 正直走 | 来源:发表于2017-05-23 14:55 被阅读149次

    首先感觉Swift的枚举确实相对OC来说功能和语法都扩展很多。首先说一下枚举的定义。

    枚举声明的类型是囊括可能状态的有限集,且可以具有附加值。通过内嵌(nesting),方法(method),关联值(associated values)和模式匹配(pattern matching),枚举可以分层次地定义任何有组织的数据。

    Swift中可以制定枚举的类型如下,Swift可以制定IntegerFloat PointString、三种关联值类型,有的资料说也布尔类型(Boolean),但本人在测试以后好像不支持,下去也会再查资料验证是否支持Boolean类型

    • 整型(Integer)
    • 浮点数(Float Point)
    • 字符串(String)
    关联Integer值类型,
    enum Direction: Int {
        case Left = 0
        case Right = 1
        case Top = 2
        case Bottom = 3
    }
    
    关联String值类型,
    enum Workday: String {
        case Mon = "Monday"
        case Tue = "Tuesday"
        case Wed = "Wednesday"
        case Thu = "Thursday"
        case Fri = "Friday"
    }
    
    关联Float Point值类型,
    enum DouNum: Double {
        case π = 3.14159
        case e = 2.71828
        case φ = 1.61803398874
        case λ = 1.30357
    }
    

    当关联IntegerString类型的时候可以忽略为枚举中的case赋值,Swift枚举也能正常工作,如下

    enum Direction: Int {
        case Left = 1
        case Right
        case Top
        case Bottom
    }
    
    enum Direction: String {
        case Sun
        case Mon
        case Tue
        case Wed
        case Thu
        case Fri
        case Sat
    }
    

    可以通过rawValue 属性来读取枚举的值,如上面的Workday枚举取出WorkDays枚举中Sun的值
    case Mon = "Monday"

     let EnumValue = WorkDays.Mon.rawValue
     print(EnumValue)
     //打印的值为- Monday
    

    不过某种情形下,你可能想要通过一个已有的 raw value
    来创建一个 enum case。这种情况下,枚举提供了一个指定构造法:

    enum Direction: Int {
        case Left = 0 
        case Right = 1 
        case Top = 2 
        case Bottom = 3
    }
    

    // 创建一个movement.Right用例,其raw value值为1,let rightMovement = Movement(rawValue: 1)

    倘若使用rawValue构造器,切记它是一个可失败构造器( failable initializer )。换言之,构造方法返回值为 可选类型值 ,因为有时候传入的值可能与任意一个 cas都不匹配。比如 Movement(rawValue:42)

    • 嵌套枚举(Nesting Enums)
      如果你有特定子类型的需求,可以对enum进行嵌套。这样就允许你为实际的enum中包含其他明确信息的enum。以RPG游戏中的每个角色为例,每个角色能够拥有武器,因此所有角色都可以获取同一个武器集合。而游戏中的其他实例则无法获取这些武器(比如食人魔,它们仅使用棍棒)。
    enum Character {
      enum Weapon {
        case Bow
        case Sword
        case Lance
        case Dagger
      }
      enum Helmet {
        case Wooden
        case Iron
        case Diamond
      }
      case Thief
      case Warrior
      case Knight
    }
    现在,你可以通过层级结构来描述角色允许访问的项目条。
    
    let character = Character.Thief
    let weapon = Character.Weapon.Bow
    let helmet = Character.Helmet.Iron
    

    说实话上面的例子我个人是无法理解的,首先枚举同级别的项目应该是统一类型的,比如特性(Character)下面的几个类别根本不是一个类型如头盔(Weapon)、武器(Helmet)、小偷(Thief)、武士(Warrior)、爵士(Knight)后三个是人物前两个是是人物的特性

    个人感觉下面的例子比较适合用嵌套枚举,如枚举的第一层是人物角色,而人物的角色下面又包含其特有的技能,比如Role包含Student,而Student又有Write技能

    enum Role {
        enum Student {
            case Sing
            case Dance
            case Write
        }
        enum Player {
            case Sing
            case Dance
            case Run
            case Jump
        }
        enum programmer {
            case Sing
            case Dance
            case Read
            case Coding
        }
        case Worker
        case Driver
    }
    

    现在,我们可以通过层级结构来描述角色允许访问的项目条。

    let worker = Role.Worker
    let code = Role.programmer.coding
    
    • 包含枚举(Containing Enums)

    同样地,你也能够在structsclasses 中内嵌枚举。接着上面的例子:

    struct Character {
         enum CharacterType {
         case Thief
         case Warrior
         case Knight
    }
      enum Weapon {
         case Bow
         case Sword
         case Lance
         case Dagger
      }
      let type: CharacterType
      let weapon: Weapon
    }
    let warrior = Character(type: .Warrior, weapon: .Sword)
    

    同样地,这也将有助于我们将相关的信息集中在一个位置。

    • 关联值(Associated Value)

    关联值是将额外信息附加到 enum case 中的一种极好的方式。打个比方,你正在开发一款交易引擎,可能存在 买 和 卖 两种不同的交易类型。除此之外每手交易还要制定明确的股票名称和交易数量:

    • 简单例程(Simple Example)
    enum Trade {
        case Buy
        case Sell
    }
    func trade(tradeType: Trade, stock: String, amount: Int) {}
    

    然而股票的价值和数量显然从属于交易,让他们作为独立的参数显得模棱两可。你可能已经想到要往 struct 中内嵌一个枚举了,不过关联值提供了一种更清爽的解决方案:

    enum Trade {
        case Buy(stock: String, amount: Int)
        case Sell(stock: String, amount: Int)
    }
    func trade(type: Trade) {}
    
    • 模式匹配(Pattern Matching)
      如果想要访问这些值,如果你想要访问这些值, 模式匹配 再次救场:
    let trade = Trade.Buy(stock: "APPL", amount: 500)
    if case let Trade.Buy(stock, amount) = trade {
        print("buy \(amount) of \(stock)")
    }
    
    • 标签(Labels)
      关联值不需要附加标签的声明:
    enum Trade {
       case Buy(String, Int)
       case Sell(String, Int)
    }
    

    倘若你添加了,那么,每当创建枚举用例时,你都需要将这些标签标示出来。

    • 元组参数(Tuple as Arguments)
      更重要的是, Swift 内部相关信息其实是一个元组,所以你可以像下面这样做:
    let tp = (stock: "TSLA", amount: 100)
    let trade = Trade.Sell(tp)
    
    if case let Trade.Sell(stock, amount) = trade {
        print("buy \(amount) of \(stock)")
    }
    // Prints: "buy 100 of TSLA"
    

    语法允许您将元组当作一个简单的数据结构,稍后元组将自动转换到高级类型,就比如 enum case 。想象一个应用程序可以让用户来配置电脑:

    typealias Config = (RAM: Int, CPU: String, GPU: String)
    
    // Each of these takes a config and returns an updated config
    func selectRAM(_ config: Config) -> Config {return (RAM: 32, CPU: config.CPU, GPU: config.GPU)}
    func selectCPU(_ config: Config) -> Config {return (RAM: config.RAM, CPU: "3.2GHZ", GPU: config.GPU)}
    func selectGPU(_ config: Config) -> Config {return (RAM: config.RAM, CPU: "3.2GHZ", GPU: "NVidia")}
    
    enum Desktop {
       case Cube(Config)
       case Tower(Config)
       case Rack(Config)
    }
    
    let aTower = Desktop.Tower(selectGPU(selectCPU(selectRAM((0, "", "") as Config))))
    

    配置的每个步骤均通过递交元组到enum
    中进行内容更新。倘若我们从 函数式编程中获得启发,这将变得更好。

    infix operator <^> { associativity left }
    
    func <^>(a: Config, f: (Config) -> Config) -> Config { 
        return f(a)
    }
    

    最后,我们可以将不同配置步骤串联起来。这在配置步骤繁多的情况下相当有用。

    let config = (0, "", "") <^> selectRAM  <^> selectCPU <^> selectGPU
    let aCube = Desktop.Cube(config)
    
    • 使用案例(Use Case Example)

    关联值可以以多种方式使用。常言道:一码胜千言, 下面就上几段简单的示例代码,这几段代码没有特定的顺序。

    // 拥有不同值的用例
    enum UserAction {
      case OpenURL(url: NSURL)
      case SwitchProcess(processId: UInt32)
      case Restart(time: NSDate?, intoCommandLine: Bool)
    }
    
    // 假设你在实现一个功能强大的编辑器,这个编辑器允许多重选择,
    // 正如 Sublime Text : https://www.youtube.com/watch?v=i2SVJa2EGIw
    enum Selection {
      case None
      case Single(Range<Int>)
      case Multiple([Range<Int>])
    }
    
    // 或者映射不同的标识码
    enum Barcode {
        case UPCA(numberSystem: Int, manufacturer: Int, product: Int, check: Int)
        case QRCode(productCode: String)
    }
    
    // 又或者假设你在封装一个 C 语言库,正如 Kqeue BSD/Darwin 通知系统:
    // https://www.freebsd.org/cgi/man.cgi?query=kqueue&sektion=2
    enum KqueueEvent {
        case UserEvent(identifier: UInt, fflags: [UInt32], data: Int)
        case ReadFD(fd: UInt, data: Int)
        case WriteFD(fd: UInt, data: Int)
        case VnodeFD(fd: UInt, fflags: [UInt32], data: Int)
        case ErrorEvent(code: UInt, message: String)
    }
    
    // 最后, 一个 RPG 游戏中的所有可穿戴装备可以使用一个枚举来进行映射,
    // 可以为一个装备增加重量和持久两个属性
    // 现在可以仅用一行代码来增加一个"钻石"属性,如此一来我们便可以增加几件新的镶嵌钻石的可穿戴装备
    enum Wearable {
        enum Weight: Int {
        case Light = 1
        case Mid = 4
        case Heavy = 10
        }
        enum Armor: Int {
        case Light = 2
        case Strong = 8
        case Heavy = 20
        }
        case Helmet(weight: Weight, armor: Armor)
        case Breastplate(weight: Weight, armor: Armor)
        case Shield(weight: Weight, armor: Armor)
    }
    let woodenHelmet = Wearable.Helmet(weight: .Light, armor: .Light)
    
    • 方法和属性(Methods and properties)
      你也可以在 enum 中像这样定义方法:
    enum Wearable {
        enum Weight: Int {
            case Light = 1
        }
        enum Armor: Int {
            case Light = 2
        }
        case Helmet(weight: Weight, armor: Armor)
            func attributes() -> (weight: Int, armor: Int) {
           switch self {
        case .Helmet(let w, let a): return (weight: w.rawValue * 2, armor: w.rawValue * 4)
           }
        }
    }
    let woodenHelmetProps = Wearable.Helmet(weight: .Light, armor: .Light).attributes()
    print (woodenHelmetProps)
    // prints "(2, 4)"
    

    枚举中的方法为每一个 enum case 而“生”。所以倘若想要在特定情况执行特定代码的话,你需要分支处理或采用switch 语句来明确正确的代码路径。

    enum Device { 
        case iPad, iPhone, AppleTV, AppleWatch 
        func introduced() -> String {
           switch self {
             case AppleTV: return "\(self) was introduced 2006"
             case iPhone: return "\(self) was introduced 2007"
             case iPad: return "\(self) was introduced 2010"
             case AppleWatch: return "\(self) was introduced 2014"
           }
        }
    }
    print (Device.iPhone.introduced())
    // prints: "iPhone was introduced 2007"
    
    • 属性(Properties)

    尽管增加一个存储属性到枚举中不被允许,但你依然能够创建计算属性。当然,计算属性的内容都是建立在枚举值下或者枚举关联值得到的。

    enum Device {
      case iPad, iPhone
      var year: Int {
        switch self {
            case iPhone: return 2007
            case iPad: return 2010
         }
      }
    }
    
    • 静态方法(Static Methods)
      你也能够为枚举创建一些静态方法(static methods )。换言之通过一个非枚举类型来创建一个枚举。举个栗子,我们在实际开发的时候,有时tableView有时需要返回两种Cell,我们可以根据后台返回的数据来判断返回那种cell表示。
    enum StatusCellID: String
    {
        case NormalCell = "NormalCell"
        case ForwardCell = "ForwardCell"
        
        /// 静态函数, 相当于类方法
        /// 根据微博数据,返回对应的标示符
        static func cellId(status: Status) ->String
        {
            return status.retweeted_status != nil ? StatusCellID.ForwardCell.rawValue : StatusCellID.NormalCell.rawValue
        }
    }
    
    • 可变方法(Mutating Methods)
      方法可以声明为mutating。这样就允许改变隐藏参数selfcase值了。
    enum TriStateSwitch { 
          case Off, Low, High 
          mutating func next() { 
          switch self { 
          case Off: self = Low 
          case Low: self = High 
          case High: self = Off 
          } 
        }
    }
    var ovenLight = TriStateSwitch.Low
    ovenLight.next()
    // ovenLight 现在等于.On
    ovenLight.next()
    // ovenLight 现在等于.Off
    
    • 小结(To Recap)

    至此,我们已经大致了解了Swift中枚举语法的基本用例。在开始迈向进阶之路之前,让我们重新审视文章开篇给出的定义,看看现在是否变得更清晰了。
    现在我们已经对这个定义更加清晰了。确实,如果我们添加关联值和嵌套, enum 就看起来就像一个封闭的、简化的 struct 。相比较 struct ,前者优势体现在能够为分类与层次结构编码。

    // Struct Example
    struct Point { let x: Int, let y: Int }
    struct Rect { let x: Int, let y: Int, let width: Int, let height: Int }
    
    // Enum Example
    enum GeometricEntity {
       case Point(x: Int, y: Int)
       case Rect(x: Int, y: Int, width: Int, height: Int)
    }
    

    方法和静态方法的添加允许我们为 enum附加功能,这意味着无须依靠额外函数就能实现。

    // C-Like example
    enum Trade {
       case Buy
       case Sell
    }
    func order(trade: Trade)
    
    // Swift Enum example
    enum Trade {
       case Buy
       case Sell
       func order()
    }
    

    *参考原文 * http://swift.gg/2015/11/20/advanced-practical-enum-examples/

    相关文章

      网友评论

        本文标题:Swift - 枚举

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