標籤:style class blog code http tar
枚舉 (Enumerations)
枚舉為一系相關聯的值定義了一個公用的群組類型.同時能夠讓你在編程的時候在型別安全的情況下去使用這些值。 如果你對C語言很熟悉,你肯定知道在C語言中枚舉類型就是一系列具有被指定有關聯名稱的的整數值.但在Swift中枚舉類型就更加靈活了,並且你不必給枚舉類型中的每個成員都賦值。如果把一個值(假設值為"raw")提供給所有的枚舉類型當中的成員,那麼這個值可以是一個字串,一個字元,一個整數或者說是一個浮點數. 作為選擇,枚舉中的成員可以被特別指定為任何不同於其他成員的可以儲存的類型,就像是其他語言當中的聯合或者變體。你能夠將一些相關聯的成員定義成一個公用的集合作為枚舉的一部分,每個部分都有不同的集合類型並且使用了恰當的類型。
枚舉文法
你可以用enum開始並且用大括弧包含整個定義體來定義一個枚舉:
enum SomeEnumeration { // 在這裡定義枚舉}
這裡有一個例子,定義了一個包含四個方向的羅盤:
enum CompassPoint { case North case South case East case West}
枚舉中定義的變數(像上例中North
, South
, East
, West
)是枚舉的成員變數(或者說成員).關鍵字case是用來標明這一行將要定義一個新的成員變數
注意:
與C或者Objective-C不同的是,在Swift語言中枚舉類型的成員初始的時候不會被預設賦值成整數值,在CompassPoint
這個例子中,North
, South
, East
, West
預設不會隱式的等於0,1,2,3。取而代之的是不同的枚舉成員將要用什麼類型以及賦值什麼值都是可以自己控制的,可以在定義CompassPoint這個枚舉的時候指定.
多個成員還可以用一行來定義,他們之間用逗號分割:
enum Plant{ case Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune}
每個枚舉的定義都是定義了一個全新的類型,就像Swfit中的其他的類型一樣,枚舉的名稱(像上邊的CompassPoint
, Plant
)應該是以一個大寫字母開頭,讓他們是單數類型而不是複數類型,從而讓他們可以不言而喻:
var directionToHead = CompassPoint.West
當directionToHead
在初始化過程中被賦值成CompassPoint
中的某一個可能的值的時候,它的類型就可以被推測出來來了。一旦directionToHead
被聲明成是CompassPoint
類型,那麼你就可以簡短的使用逗號運算式來給它賦值成其他的CompassPoint
當中的值了:
directionToHead = .East
directionToHead
的類型是已知的了,所以你可以忽略它的類型來給他賦值了。這樣使得在使用顯示類型的枚舉值時代碼具有很高的可讀性。
使用Switch語句來匹配枚舉值
你可以通過switch語句來訪問單獨的某個枚舉值:
directionToHead = .Southswitch 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")}// 輸出"Watch out for penguins”
你可以這樣閱讀這段代碼:
考慮directionToHead
的值,如果它等於.North
那麼就輸出"Lost of planets have a north",如果它等於.South
,那麼就輸出"Watch out for penguins"。
就和在控制流程程那一章所講,一個switch語句被用到判斷枚舉值的時候,必須要包括所有的枚舉成員。假設.West
被忽略了,將會導致編譯出錯,因為它沒有考慮到枚舉的所有的枚舉成員,我們需要全面性的確保枚舉的所有成員不被忽略掉.
如果給考慮每個枚舉的成員不合適,你可以提供一個default來覆蓋其他沒有明確處理的成員:
let somePlanet = Planet.Earthswitch somePlanet { case .Earth: println("Mostly harmless") default: println("Not a safe place for humans")}// 輸出 "Mostly harmless"
關聯值
在上一節的樣本中顯示了一個枚舉的成員是如何在自己的權利界定(和類型)的值。你可以設定一個常量或變數的值為Planet.Earth
,然後檢查這個值。然而,如果在保留成員值的同時能夠儲存其它類型的關聯值將會變得更有意義。這使您能夠在儲存成員值的同時儲存額外的自訂資訊,並且允許每次你在代碼中使用這些成員值的時候改變這些關聯值。在Swift中當你定義一個枚舉成員的時候,你可以給他關聯任何的類型,而且如果需要的話每個成員可以有不同的關聯類別型。枚舉類型的這個特性和其他語言當中的辨別聯合,標記聯合或者變體很像。
舉個例子,設想一個庫存跟蹤系統想要通過兩種不同的條碼來跟蹤產品。一些產品用UPC-A格式的一維條碼標識的,使用0到9的數字。每個條碼當中有一個標識“數字系統“的數字,然後是10個“標識符"數字,最後邊一個用來做“檢查”的數字,以確保這個條碼被正確的掃描識別:
另一些產品是用QR編碼格式的二維碼標識的,這種條碼可以使用任何ISO 8859-1的字元而且最大可以編碼一個2953字元長度的字串:
如果能夠用一個有三個整型的元組來儲存UPC-A格式的條碼,然後用一個可以儲存任意長度的字串來儲存QR格式的條碼,那麼對於一個庫存跟蹤系統來說,將會是再便捷不過的了。
在Swift語言中,一個可以定義兩個格式的產品條碼的枚舉看起來是這樣的:
enum Barcode { case UPCA(Int, Int, Int) case QRCode(String)}
你可以這樣閱讀這段代碼:
定義了一個叫做Barcode的枚舉類型,它可以有一個UPCA成員,這個成員關聯了一個包含三個整型數值的元組,同時這個枚舉類型還有一個QRCode成員,關聯了一個字串。
這個定義不會產生任何的整型或者字串值,他只是定義了當一個不可變變數或者變數等於Barcode.UPCA或者Barcode.QRCode
的時候它被關聯的值的類型
這樣一來可以用任意其中一個類型來產生一個新的條碼了:
var productBarcode = Barcode.UPCA(8, 85909_51226, 3)
這個例子產生了一個新的變數叫做productBarcode
,這個變數被關聯了一個Barcode.UPCA
類型的元組,這個元組的值為(8, 8590951226, 3)
,被提供做“標識”的值用底線分割了-85909_51226,這樣做是為了更好的被以條碼讀出來.
同樣的產品還可以被賦值為另一個條碼類型:
productBarcode = .QRCode("ABCDEFGHIJKLMNOP")
在這一點上,原來的Barcode.UPCA
以及關聯到的整型值被一個新類型Barcode.QRCode
以及與其關聯的字串給替換了。Barcode
類型的不可變變數以及可變變數可以儲存.UPCA
或者.QRCode
類型(同時還有與其相關聯的值),但是每次都只能儲存這兩個類型當中的一個。
同樣,這兩個不同的類型可以用Switch語句來做檢查。同時,在switch語句中他們相關聯的值也可以被擷取到。你可以把關聯的值當做不可變變數(用let
來開頭),或者可變變數(用var
開頭)以在switch的控制體當中使用。
switch productBarcode { case .UPCA(let numberSystem, let identifier, let check): println("UPC-A with value of \(numberSystem), \(identifier), \(check).") case .QRCode(let productCode): println("QR code with value of \(productCode).")}
如果一個枚舉成員關聯的所有值都被當做不可變變數或者可變變數來使用,那麼你可以在成員名稱之前只放一個let
或者var
來達到目的,簡要樣本:
switch productBarcode {case let .UPCA(numberSystem, identifier, check): println("UPC-A with value of \(numberSystem), \(identifier), \(check).")case let .QRCode(productCode): println("QR code with value of \(productCode).")}// 輸出 "QR code with value of ABCDEFGHIJKLMNOP.”
原始值
在上一節當中條碼的例子展示了一個枚舉類型的成員怎麼聲明他們可以儲存不同類型的關聯值。不同於關聯值,枚舉類型的成員還可以預設定預設值(我們叫他原始值),這些值的類型是相同的。
這裡有一個枚舉成員儲存一個ASCII值的例子:
enum ASCIIControlCharacter: Character { case Tab = "\t" case LineFeed = "\n" case CarriageReturn = "\r"}
這裡定義了一個原始值為字元類型的枚舉類型ASCIIControlCharacter
,而且包含了一些我們常用的控制字元。關於字元類型,你可以在字串和字元那個章節找到更多的描述。
請注意,原始值與關聯值不同。原始值應該是在你定義枚舉的代碼中被設定為預填儲值的,就像上述三個ASCII碼。對於一個特定的枚舉成員的原始值始終是相同的。關聯值是當你建立一個基於枚舉的成員的新的常量或變數的時候設定的,並且每次都可以是不同的。
原始值可以是字串,字元或者其他任何的整型或者浮點型等數字類型。每個原始值在他屬的枚舉類型定義的時候都應該是不同的。如果原始值是整數類型,那麼當其他枚舉成員沒有設定原始值的時候,他們的原始值是這個整型原始值自增長設定的。
下邊這個枚舉類型是對之前的Plant
枚舉類型的改良,新的枚舉類型有一個整型的原始值來標識他們在距離太陽的順序:
enum Planet: Int { case Mercury = 1, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune}
自增長的意思就是Planet.Venus
的原始值會被設定成2,以此類推。
可以通過枚舉成員的toRaw()方法來擷取他的原始值:
let earthsOrder = Planet.Earth.toRaw()//eathsOrder is 3
使用枚舉成員的fromRaw()方法來嘗試通過一個原始值來尋找他所對應的枚舉成員。下面這個例子介紹了怎麼通過Uranus的原始值7找到Uranus的:
let possiblePlanet = Planet.fromRaw(7)// possiblePlanet is of type Planet? and equals Planet.Uranus”
然而不是所有的整型值都可以找到一個對應的Planet
,正是如此,fromRaw()
將會返回一個非強制類型的枚舉成員。上邊展示的例子當中possiblePlanet
是一個Planet?
類型,或者是一個非強制Planet
類型。
如果你嘗試通過原始值9來尋找他對應的Planet
,那麼fromRaw()
返回給你的非強制Planet
類型將會是nil
:
let positionToFind = 9if let somePlanet = Planet.fromRaw(positionToFind) { switch somePlanet { case .Earth: println("Mostly harmless") default: println("Not a safe place for humans") }} else { println("There isn‘t a planet at position \(positionToFind)")}// 輸出 "There isn‘t a planet at position 9”
這個例子嘗試使用非強制類型綁定來試著用原始值9來擷取一個Planet
。運算式if let somePlanet = Planet.fromRaw(9)
用來檢索一個Planet
,如果能夠檢索到Planet
將會將這個非強制Planet
賦值給somePlanet
。在這個樣本中,通過原始值9不可能檢索到對應的Planet
,所以else
包含的語句將會被執行。
Swift QQ群
Swift互助交流QQ群:74512850,讓愛好者共同進步!群內有完整版中文PDF文檔!
點選連結加入群【SWIFT開發互助】:http://jq.qq.com/?_wv=1027&k=NwGksV
中文手冊導航
部落格園:點擊進入導航
GitHub:點擊進入導航