Swift 枚举

语言: CN / TW / HK

原文地址

枚举是为一组相关值定义的一个通用类型,Swift 枚举的功能非常强大,使它比 OC 的枚举应用于更广泛的场景。在本文中,我们会了解 enum 的语法和能力。

枚举语法

使用 enum 关键字引入枚举,其整个定义放在一对大括号内:

enum CompassPoint { case north case south case east case west }

与 C 和 Objective-C 不同,Swift 的枚举成员在创建时不会被赋予一个默认的整型值。

枚举成员遍历

令枚举遵循 CaseIterable 协议。Swift 会生成一个 allCases 属性,用于返回枚举中所有成员的集合。

``` enum Beverage: CaseIterable { case coffee, tea, juice }

for beverage in Beverage.allCases { print(beverage) }

// coffee // tea // juice ```

关联值

关联值是将附加信息附加到枚举成员的一种非常好的方式。Swift 枚举可以存储任意类型的关联值,例子如下:

``` enum Barcode { case upc(Int, Int, Int, Int) case qrCode(String) }

```

上面的代码定义一个名为 Barcode 的枚举类型,其中一个成员 upc 具有(Int, Int, Int, Int) 类型的关联值,另一个成员 qrCode 具有 String 类型的关联值。

可以通过模式匹配来访问关联值,例子如下:

``` var productBarcode = .qrCode("ABCDEFGHIJKLMNOP") switch productBarcode { case let .upc(numberSystem, manufacturer, product, check): print("UPC: (numberSystem), (manufacturer), (product), (check).") case let .qrCode(productCode): print("QR code: (productCode).") }

// 打印“QR code: ABCDEFGHIJKLMNOP.” ```

关于模式匹配的详细使用会在后面的文章介绍。

原始值

枚举成员可以被默认值预填充,这些默认值被称为原始值,注意:原始值的类型必须相同

enum ASCIIControlCharacter: Character { case tab = "\t" case lineFeed = "\n" case carriageReturn = "\r" }

默认情况下,Swift 只支持下面4种类型的枚举值:整型、浮点数、字符串、布尔值。如果要支持更多类型的枚举值,需要实现指定的协议,这个在后面的文章中会讲到。

原始值的隐式赋值

在使用的原始值是整数或字符串类型时,Swift 可以为你自动赋值。

  • 当使用的原始值是整数时,隐式赋值的值会一次增加1。如果第一个枚举成员没有设置原始值,其默认原始值是 0。

``` enum Planet: Int { case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune }

// Plant.mercury 的显式原始值为 1,Planet.venus 的隐式原始值为 2,依此类推。 ```

  • 当使用字符串作为枚举成员的原始值时,每个枚举成员的隐式原始值是该成员的名字。

``` enum CompassPoint: String { case north, south, east, west }

// CompassPoint.south 拥有隐式原始值 south,依此类推。 ```

可以使用 rawValue 属性访问枚举成员的原始值:

``` let earthsOrder = Planet.earth.rawValue // earthsOrder 值为 3

let sunsetDirection = CompassPoint.west.rawValue // sunsetDirection 值为 "west" ```

使用原始值初始化枚举实例

如果在定义枚举时使用了原始值,那么会自动生成一个初始化方法,这个方法有一个 rawValue 参数,参数类型即为原始值类型:

let possiblePlanet = Planet(rawValue: 7) // possiblePlanet 类型为 Planet? 值为 Planet.uranus

原始值构造器是一个可失败的构造器,因为并不是每一个原始值都有一个与之对应的枚举成员,因此其返回类型是可选类型。

递归枚举

递归枚举是一种枚举类型,它的一个或多个成员使用该枚举类型作为关联值。在枚举成员前加上 indirect 关键字表示该成员可递归:

enum ArithmeticExpression { case number(Int) indirect case addition(ArithmeticExpression, ArithmeticExpression) indirect case multiplication(ArithmeticExpression, ArithmeticExpression) }

也可以在枚举类型开头加上 indirect 来表明其所有成员都可递归:

indirect enum ArithmeticExpression { case number(Int) case addition(ArithmeticExpression, ArithmeticExpression) case multiplication(ArithmeticExpression, ArithmeticExpression) }

其使用如下:

``` let five = ArithmeticExpression.number(5) let four = ArithmeticExpression.number(4) let sum = ArithmeticExpression.addition(five, four) let product = ArithmeticExpression.multiplication(sum, ArithmeticExpression.number(2))

func evaluate(_ expression: ArithmeticExpression) -> Int { switch expression { case let .number(value): return value case let .addition(left, right): return evaluate(left) + evaluate(right) case let .multiplication(left, right): return evaluate(left) * evaluate(right) // (5 + 4) * 2 } }

print(evaluate(product))

// 打印“18” ```

嵌套枚举

Swift 中允许枚举嵌套,我们在使用时,需要确保一个枚举类型是从属于另一个枚举类型的,否则会引起语义混乱。下面看一个例子:

``` 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 ``` 访问嵌套枚举成员时,不必每次都输入这么长的层级来访问,可以使用便捷方法直接访问,如下:

``` func strength(of character: Character,

          with weapon: Character.Weapon,

          and armor: Character.Helmet) {

}

strength(of: .thief, with: .bow, and: .wooden) ```

包含枚举

你也可以在结构或类中嵌入枚举。继续前面的例子:

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

方法和属性

Swift 枚举类型可以附加方法和属性:

方法

``` enum Transportation { case car(Int) case train(Int)

func distance() -> String {
 switch self {
 case .car(let miles): return "\(miles) miles by car"
 case .train(let miles): return "\(miles) miles by train"
 }

} } ``` 与结构或类类型的主要区别在于,可以在方法中使用 self 来计算输出。

属性

注意:Swift 中不允许添加存储属性,下面的代码会编译报错:

``` enum Device { case iPad case iPhone

let introduced: Int // Enums must not contain stored properties } ```

你应该使用计算属性,如下:

``` enum Device { case iPad, case iPhone

var introduced: Int {
    switch self {
    case .iPhone: return 2007
    case .iPad: return 2010
 }

} } ```

静态方法

静态方法是可以通过类型名称而不是类型的特定实例调用的方法。例子如下:

``` enum Device { static var newestDevice: Device { return .appleWatch }

case iPad,
case iPhone
case appleWatch

} ```

可变方法

通过 mutating 关键字将方法声明为可变方法,在可变方法中可以修改 self 实例,例子如下:

``` enum TriStateSwitch { case off, low, bright mutating func next() { switch self { case .off: self = low case .low: self = .bright case high: self = off } } }

var ovenLight = TriStateSwitch.low

ovenLight.next() // ovenLight is now equal to .bright

ovenLight.next() // ovenLight is now equal to .off ```

小结

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

参考: 1. http://appventure.me/guides/advanced_practical_enum_examples/diving_in/intro.html 2. http://docs.swift.org/swift-book/LanguageGuide/Enumerations.html