当为只能取一组特定可能状态的数据建模时(比如星期几、指南针方向,或手柄上的按钮),像Int或String这样的基本类型有明显缺点。使用整数或字符串时,代码必须不断检查无效值(比如状态码999,或拼写成"leftt"这样的错误),而读者也无法一眼看出这些数字代表什么。
Swift 用枚举(也常简称为枚举)解决这个问题。枚举为一组相互关联、有名字的值定义一种共同的类型。因为编译器提前知道所有可能的成员,所以能在编译时检查代码是否正确和完整。枚举还可以有方法、构造器和计算属性。
使用enum关键字定义枚举,后面跟上类型名,并在大括号内列出各个成员。按照 Swift 的约定,类型名使用UpperCamelCase,而每个成员使用lowerCamelCase:
enum NESButton {
case up
case down
case left
case right
case a
case b
case select
case start
}
也可以在一行中定义多个成员,用逗号分隔:
enum NESButton {
case up, down, left, right, a, b, select, start
}
要引用某个枚举成员,请写出枚举类型名、一个点号(.)以及成员名。一旦类型已知,或者编译器能够推断出来,就可以省略类型名,使用更简短的点语法:
var lastPressed = NESButton.up
// Swift infers the type, so you can omit the type name:
lastPressed = .down
let konamiCode: [NESButton] = [.up, .up, .down, .down, .left, .right, .left, .right, .b, .a]
枚举可以定义方法和自定义构造器,就像类和结构体那样。在枚举方法内部,self指的是当前成员值:
enum Coin {
case heads
case tails
init(_ value: Int) {
if value.isMultiple(of: 2) {
self = .heads
} else {
self = .tails
}
}
func flip() -> Coin {
switch self {
case .heads: return .tails
case .tails: return .heads
}
}
}
let tails = Coin(13) // .tails
let heads = Coin(0) // .heads
let flipped = heads.flip() // .tails
枚举成员可以预先填充默认值,这些默认值称为原始值。一个枚举中的所有原始值必须共享同一类型(比如Int、String或Character):
enum NESButton: Character {
case up = "⬆️"
case down = "⬇️"
case left = "⬅️"
case right = "➡️"
case a = "🅰️"
case b = "🅱️"
case select = "✅"
case start = "🚦"
}
print(NESButton.left.rawValue) // Prints "⬅️"
可以使用init(rawValue:)从原始值初始化枚举。因为可能提供无效的原始值,所以这个构造器返回一个可选值:
let upButton = NESButton(rawValue: "⬆️") // Optional(NESButton.up)
let invalid = NESButton(rawValue: "🙄") // nil
Swift 可以为String和Int枚举自动分配原始值:
String枚举,每个成员的原始值默认为其成员名。Int枚举,原始值从0开始,除非另有指定,否则每次自增 1。enum Planet: Int {
case mercury = 1, venus, earth, mars
}
print(Planet.earth.rawValue) // Prints 3
在这个练习中,你将处理日志行。
每条日志行都是一个格式如下的字符串:"[<LVL>]: <MESSAGE>"。
日志级别有以下几种:
TRC (trace)DBG (debug)INF (info)WRN (warning)ERR (error)FTL (fatal)本练习共有三个任务,需要逐一完成,才能实现完整的功能。
定义LogLevel枚举,它包含六个枚举成员,分别对应上面的日志级别,另外再加一个未知成员,用于表示缺失或非标准的日志级别。
tracedebuginfowarningerrorfatalunknown接下来,实现LogLevel初始化方法,用它解析日志行的日志级别:
LogLevel("[INF]: File deleted")
// Returns LogLevel.info
LogLevel("Something went wrong!")
// Returns LogLevel.unknown
日志行里的日志级别相当冗长。
为了减少存储日志行所需的磁盘空间,人们设计了一种短格式:"<ENCODED_LEVEL>:<MESSAGE>"。
编码后的日志级别就是把日志级别简单映射成一个数字:
trace - 0
debug - 1
info - 4
warning - 5
error - 6
fatal - 7
unknown - 42
实现shortFormat(message: String) -> String方法,它能将日志行以缩短后的格式输出为String:
let overflow = LogLevel.error
overflow.shortFormat(message: "Stack overflow")
// Returns "6:Stack overflow"