swift_031(Swift 中的列舉/原始值(RawValues))
列舉定義語法
首先,我們來看看在 swift 中定義列舉的語法:
enum WeekDay {
case Monday
case Tuesday
case Wednesday
case Thursday
case Friday
case Saturday
case Sunday
}
我們注意到,swift 的每個列舉項前面,都使用一個 case
關鍵字來標識。除了每行宣告一個列舉項,也可以將這些列舉項放在一行中宣告,每項之間用逗號分隔。
enum WeekDayInSingleLine {
case Monday,Tuesday ,Wednesday,Thursday,Friday,Saturday,Sunday
}
注意一點,Objective-C 和 C 語言那樣, swift 中的列舉項不可以用 0,1,2 這樣的數字值來代替。它們有自己的值。
列舉型別定義好之後,我們就可以將它的列舉值賦值給某個變數:
var weekday = WeekDay.Tuesday
並且,對於型別明確的變數,我們可以直接省去列舉的型別字首:
var day:WeekDay = .Wednesday
列舉的使用
列舉值可以在 switch
語句中進行匹配:
switch weekday {
case .Monday:
println (":(")
case .Tuesday:
println(":(")
case .Wednesday:
println(":(")
case .Thursday:
println(":(")
case .Friday:
println(":|")
case .Saturday:
println(":)")
case .Sunday:
println(":)")
}
在 switch
語句中的每個 case
中,我們提供各個列舉項的名稱:.Monday
,.Tuesday
等等。在 swift 中 switch 中匹配列舉項,必須顯示的列舉出所有的列舉項。也就是對於我們上面表示星期的列舉型別WeekDay
switch
語句中必須將所有的列舉項分支都明確的寫出來。否則就會有編譯錯誤。
這個機制也體現了 Swift 型別安全的核心思想。如果我們覺得每個列舉項都要明確的指定行為比較麻煩,我們還可以使用
default
分支來對於其餘的列舉項定義行為:
switch weekday {
case .Saturday:
println(":)")
case .Sunday:
println(":)")
default:
println(":(")
}
總之,無論用 default
也好,還是明確對每一個列舉項指定行為也好,在 Swift 中,我們都必須對列舉型別下的每個值,指定確定的行為。不能漏掉其中任何一個可能性。
關聯值(Associated Values)
在 Swift 中,我們還可以定義這樣的列舉型別,它的每一個列舉項都有一個附加資訊,來擴充這個列舉項的資訊表示,這又叫做關聯值。加入我們有一個列舉型別Shape
來表示形狀。
這個形狀可以是矩形,也可以是圓形,等等。而每種具體的形狀又對應了不同的屬性,比如矩形有長,寬,圓形有,圓心,半徑,等等。那麼列舉的關聯值就可以幫我們解決這個問題:
enum Shape {
case Rectangle(CGRect)
case Circle(CGPoint,Int)
}
我們看到,每個列舉項的後面,都包含了一對括號,這裡面定義了這個列舉項的關聯值的型別。對於 Rectangle
我們使用一個
CGRect
來表示他的原點和長寬屬性。
而對於 Circle
,我們使用一個包含了 CGPoint
和 Int
型別的元組(Tuple) 來表示這個圓的圓心和半徑。
這樣我們在初始化列舉型別的時候,我們就可以根據每個列舉項的關聯值型別,為它指定附加資訊了:
var rect = Shape.Rectangle(CGRectMake(0, 0, 200, 200))
var circle = Shape.Circle(CGPointMake(25, 25), 20)
這樣的列舉用法,是不是覺得非常方便呢, 有木有腦洞小開的感覺呢,嘿嘿~
我們再看一下,帶有關聯值的列舉項在 switch
語句中的用法:
switch(rect) {
case .Rectangle(let rect):
println("this is a rectangle at \\\\(rect)")
case let .Circle(center, radius):
println("this is a circle at \\\\(center) with radius \\\\(radius)")
}
我們在 case
後面用一對括號來輸出列舉項的關聯值,可以用 let
或者 var
關鍵字,分別作為常量和變數進行輸出。我們這裡這樣來使用case .Rectangle(let rect)
。對於關聯值是包含多個值的元組型別的,我們可以將
let
關鍵字放置在列舉項型別的前面,這樣就可以不用對每個關聯值都宣告let
關鍵字了,let .Circle(center, radius)。
原始值(Raw Values)
我們剛剛瞭解了關聯值型別的列舉的使用,Swift 的列舉型別還提供了另外一個叫做原始值(Raw Values)的實現。和關聯值不同,它為列舉項提供一個預設值,這個預設值是在編譯的時候就確定的。而不像關聯值那樣,要再實力化列舉值的時候才能確定。
這也就是說,原始值對於同一個列舉項都是一樣的。而關聯值對於同一個列舉項只是值的型別相同,但具體的取值也是不同的。
下面我們來看一下定義列舉原始值 (Raw Values) 的方法:
enum WeekDayWithRaw : String {
case Monday = "1. Monday"
case Tuesday = "2. Tuesday"
case Wednesday = "3. Wednesday"
case Thursday = "4. Thursday"
case Friday = "5. Friday"
case Saturday = "6. Saturday"
case Sunday = "7. Sunday"
}
還是表示星期的列舉型別,我們對每個列舉項都定義了一個預設的原始值,注意一下我們定義列舉的第一行程式碼,enum WeekDayWithRaw : String
我們在列舉定義的最後,多加了一個String
關鍵字,這就表示這個列舉的原始值(Raw Values) 是 String
型別的。
在我們下面的定義中,也體現了這一點。對於所有的列舉項,我們賦給的原始值都是 String
型別的。
定義好了原始值後,我們就可以用列舉項的 rawValue
屬性來輸出它:
println(WeekDayWithRaw.Saturday.rawValue) //6. Saturday
我們還可以通過原始值(Raw Values) 來初始化列舉型別:
let day = WeekDayWithRaw(rawValue: "3. Wednesday")
這個初始化方法的返回值是一個 Optionals。所以我們可以用 Optionals 的組合鏈來使用它的返回值:
if let day = WeekDayWithRaw(rawValue: "3. Wednesday") {
println(day)
}else{
println("init fail")
}
返回值為 Optionals 的型別,代表這個方法可以返回一個具體的值,也可以返回 nil
, 因為我們傳入初始化方法的原始值,可能會不等於我們預設的那幾個值,比如我們這樣初始化一個列舉:
let day = WeekDayWithRaw(rawValue: "No Exist Value")
如上面所示,"No Exist Value" 這個值和我們定義個原始值列表中任何一項都不對應,所以這個初始化是會失敗的,在這種情況下初始化方法會返回 nil,來表示初始化失敗。
所以基於這種情況,我們需要對返回值進行判斷,這也就是我們上面的 if 判斷的用處所在:
if let day = WeekDayWithRaw(rawValue: "3. Wednesday") {
println(day)
}else{
println("init fail")
}