當你要為只能取特定幾種可能狀態的資料建立模型時,例如一週中的每一天、羅盤方向,或控制器上的按鈕,像Int或String這類基本型別有很大的缺點。
使用整數或字串時,你的程式碼必須不斷檢查無效的值(例如狀態碼999,或"leftt"這種拼錯的字),而人類讀者也無法輕易看出這些數字代表什麼。
Swift 用列舉(Enumerations)(通常簡稱為列舉)解決了這個問題。 列舉為一組相關的具名值定義了一個共同的型別。因為編譯器事先就知道所有可能的成員,所以能在編譯時期檢查你的程式碼是否正確且完整。 列舉也可以有方法、初始化器和計算屬性。
使用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:)從原始值初始化列舉。由於可能傳入無效的原始值,這個初始化器會回傳一個 Optional:
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 列舉,其中包含六個對應上述日誌等級的 case,以及一個用於缺少或非標準日誌等級的 unknown case。
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"