枚举
一个枚举为一组相关联的值定义一个通用类型,并且让你可以在代码中类型安全地操作这些值。
C中的枚举将关联的名字指派给一系列整型值。Swift中的枚举类型更为活泼,并不需要为每个成员指定值,如果指定值(raw value),这个值可以是String或者Character、整型或者浮点型。
此外,每个枚举成员可以指定不同数量的任意类型的关联值存储起来。这有点像其他语言里的联合。你可以定义一个普通的一系列关联值的集合作为一个枚举的一部分,每个枚举都可以有一个适当类型的不同集合。
在Swift中,枚举类型是一等类型(first class),他们拥有很多以前只有类才拥有的特性,比如属性用以给枚举的当前值提供额外的信息,以及实例方法给枚举所代表的值提供操作。枚举类型还可以提供初始化方法来提供初始化成员值,也可以被扩展来增加原始操作之外的操作,还可以遵守协议来提供标准操作。
枚举语法
用enum关键字来创建枚举,将所有的定义放在一对大括号内部:
enum CompassPoint { case North case South case East case West }
case关键字意味着一行新的成员值就要被定义了,和C语言不一样的是,Swift中的枚举成员并不会在创建的时候就被赋给一个整型值,上面的例子中,North并不隐式地等于0,它们都是完全合法的值,都是一个明确定义的数据类型CompassPoint。
同一个case行可以有多个值,用逗号隔开即可。
像Swift中的其他数据类型名称一样,枚举型的名字应该以大写字母开头。
当把枚举成员赋值给某个变量后,这个变量的数据类型就可以被推断出为这个枚举类型,此后可以用简写的点语法来改变其值了:
var directionToHead = CompassPoint.West directionToHead = .East
用switch语句匹配枚举值:
directionToHead = .South switch directionToHead { case .North: println("Lots of planets have a north") case .South: println("Watch out for penguins") case .East: println("Where the sun rises") case .West: println("Where the skies are blue") } // prints "Watch out for penguins”
正如之前控制流中介绍的,当用switch语句处理枚举类型时,它必须全面的,比如如果省略掉case .West,那么就无法编译,因为switch语句并没有考虑到完整的枚举成员。这就防止了不小心漏掉了枚举成员。当然,在无法覆盖所有枚举成员的情况下,可以用一个default分支来处理。
关联值
前面的例子中,枚举成员都是自己本身作为自己的值。此外,还可以把变量/常量赋值给枚举成员,然后在其他地方使用它。但是,有时候会需要依靠这些成员值存储一些其他类型的关联值,这使得你可以依靠这些成员值存储额外的自定义信息,并且在每次使用成员时允许这个信息是不一样的。
你可以定义枚举存储任何已经定义的数据类型的关联值,并且每个枚举成员的关联值可以是不同类型的。比如下面的例子,定义了库存码的枚举类型,它有两个成员,一个代表一维条码(它总是可以用4个整型数字来标识),一个代表二维码(它总是可以用一个字符串来标识):
enum Barcode { case UPCA(Int, Int, Int, Int) case QRCode(String) }
这里,枚举成员都有各自的关联值,定义的时候只需要指明关联值的类型,不需要给定值。这样,新的Barcode类型变量就可以创建为:
var productBarcode = Barcode.UPCA(8, 85909, 51226, 3)
这里创建了一个变量,赋给它的值是一个有用关联元组(8, 85909, 51226, 3)的Barcode.UPCA,在此之后,这个变量就可以被赋值为不同的Barcode类型值了,比如:
productBarcode = .QRCode("ABCDEFGHIJKLMNOP")
不同的Barcode类型仍然可以像之前那样在switch语句中被检查,这次,关联值可以被取出来成为switch语句的一部分。可以将关联值作为常量或者变量取出来以便在case体内部使用:
switch productBarcode { case .UPCA(let numberSystem, let manufacturer, let product, let check): println("UPC-A: /(numberSystem), /(manufacturer), /(product), /(check).") case .QRCode(let productCode): println("QR code: /(productCode).") } // prints "QR code: ABCDEFGHIJKLMNOP.”
如果所有的关联值都是作为常量/变量被取出,则不需要在每个关联值前都写上var/let,将关键字提到前面:
switch productBarcode { case let .UPCA(numberSystem, manufacturer, product, check): println("UPC-A: /(numberSystem), /(manufacturer), /(product), /(check).") case let .QRCode(productCode): println("QR code: /(productCode).") } // prints "QR code: ABCDEFGHIJKLMNOP.”
原始值(Raw Values)
如前所述,枚举成员可以定义不同类型的关联值。此外,还可以枚举成员也可以给定默认值(被称为raw value),这些值必须是同一类型的。比如:
enum ASCIIControlCharacter: Character { case Tab = "/t" case LineFeed = "/n" case CarriageReturn = "/r" }
注意原始值和关联值之间的区别,原始值是在定义枚举类型的时候就给出的,并且之后始终保持一致,关联值在定义枚举类型的时候并没有给出,而是在创建枚举类型变量的时候才赋予常量或者变量值。
原始值可以是字符串、字符、整数、浮点数。每个原始值必须在枚举声明内部是唯一的。如果原始值是整形的,那么如果有的成员没有赋原始值,系统会自动自增整形值赋给它们。比如:
enum Planet: Int { case Mercury = 1, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune }
这时系统会自动给后续成员依次赋原始值。通过rawValue可以获取成员的初始值,比如:
let earthsOrder = Planet.Earth.rawValue // earthsOrder is 3
如果定义了一个有原始值类型的枚举类型,枚举会自动得到一个接受原始值类型参数并返回一个枚举成员或者nil的初始化方法,可以使用该方法视图创建一个枚举实例:
let possiblePlanet = Planet(rawValue: 7) // possiblePlanet is of type Planet? and equals Planet.Uranus
并不是所有的整型数都能在枚举内部找到匹配的成员,所以,这个初始化方法返回的是可选枚举成员(optional enumeration member),