700
技術社區[雲棲]
Swift學習之十六:枚舉(Enumeration)
Swift中的枚舉與C、OC中的枚舉有很大的區別,增加了原來類中都有的特性到枚舉中。
定義枚舉的語法:
enum SimeEnum { // enumeration goes here }
定義一個指南針的四個方向的枚舉:
// 使用case來表示新成員的定義開始 enum CompassPoint { case North case South case East case West }
// 使用枚舉
var direction = CompassPoint.West// 再次修改時,可以不指定枚舉,而可以隻使用.引用
direction = .North
下麵是引用 BY 史薇芙特 · 2014年6月5日
枚舉定義了一個常用的具有相關性的一組數據,並在你的代碼中以一個安全的方式使用它們。
如果你熟悉C語言,你就會知道,C語言中的枚舉指定相關名稱為一組整數值。在Swift中枚舉更為靈活,不必為枚舉的每個成員提供一個值。如果一個值(被稱為“原始”的值)被提供給每個枚舉成員,則該值可以是一個字符串,一個字符,或者任何整數或浮點類型的值。
另外,枚舉成員可以指定任何類型,每個成員都可以存儲的不同的相關值,就像其他語言中使用集合或變體。你還可以定義一組通用的相關成員為一個枚舉,每一種都有不同的一組與它相關的適當類型的值的一部分。
在Swift中枚舉類型是最重要的類型。它采用了很多以前隻有類才具有的特性,如計算性能,以提供有關枚舉的當前值的更多信息,方法和實例方法提供的功能相關的枚舉表示的值傳統上支持的許多功能。枚舉也可以定義初始化,以提供一個初始成員值;可以在原有基礎上擴展擴大它們的功能;並使用協議來提供標準功能。
欲了解更多有關這些功能,請參見Properties, Methods, Initialization, Extensions, Protocols
1、枚舉語法
使用枚舉enum關鍵詞並把他們的整個定義在一對大括號內:
1 2 3 |
enum SomeEnumeration { // enumeration definition goes here } |
下麵是一個指南針的四個點一個例子:
1 2 3 4 5 6 |
enum CompassPoint { case North case South case East case West } |
在枚舉中定義的值(如North,South,East和West)是枚舉的成員值(或成員)。這個例子裏case關鍵字表示成員值一條新的分支將被定義。
Note
不像C和Objective-C,Swift枚舉成員在創建時不分配默認整數值。在上麵的例子CompassPoints中North,South,Eath,West不等於隱含0,1,2和3,而是一種與CompassPoint明確被定義的類型卻各不相同的值。
多個成員的值可以出現在一行上,用逗號分隔:
1 2 3 |
enum Planet { case Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune } |
每個枚舉定義中定義了一個全新的類型。像其他Swift的類型,它們的名稱(如CompassPoint和Planet)應為大寫字母。給枚舉類型單數而不是複數的名字,這樣理解起來更加容易如:
1
|
var directionToHead = CompassPoint.West |
使用directionToHead的類型時,用CompassPoint的一個可能值初始化的推斷。一旦directionToHead被聲明為一個CompassPoint,您可以將其設置為使用更短的.語法而不用再書寫枚舉CompassPoint值本身:
1
|
directionToHead = .East |
directionToHead的類型是已知的,所以你可以在設定它的值時,不寫該類型。使用類型明確的枚舉值可以讓代碼具有更好的可讀性。
2、匹配枚舉值與switch語句
你可以使用單個枚舉值匹配switch語句:
1 2 3 4 5 6 7 8 9 10 11 12 |
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" |
你可以理解這段代碼:
“考慮directionToHead的價值。當它等於North,打印“Lots of planets have a north”。當它等於South,打印“Watch out for penguins”等等。
正如控製流所描述,Switch語句考慮枚舉的成員,如果省略了West時,這段代碼無法編譯,因為它沒有考慮CompassPoint成員的完整性。Switch語句要求全麵性確保枚舉成員,避免不小心漏掉情況發生。
當它不需要為每一個枚舉成員都匹配的情況下,你可以提供一個默認default分支來涵蓋未明確提到的任何成員:
1 2 3 4 5 6 7 8 |
let somePlanet = Planet.Earth switch somePlanet { case .Earth: println("Mostly harmless") default: println("Not a safe place for humans") } // prints "Mostly harmless" |
3、關聯值
在上一節中的示例延時了一個枚舉的成員是如何被定義(分類)的。你可以為Planet.Earth設置一個常量或變量,然後在代碼中檢查這個值。但是,它有時是有用的才能存儲其它類型的關聯值除了這些成員的值。這讓你隨著成員值存儲額外的自定義信息,並允許在你的代碼中來使用該信息。
Swift的枚舉類型可以由一些數據類型相關的組成,如果需要的話,這些數據類型可以是各不相同的。枚舉的這種特性跟其它語言中的奇異集合,標簽集合或者變體相似
例如,假設一個庫存跟蹤係統需要由兩種不同類型的條形碼來跟蹤產品。有些產品上標有UPC-A代碼格式,它使用數字0到9的一維條碼,每一個條碼都有一個“數字係統”的數字,後跟十“標識符”的數字。最後一位是“檢查”位,以驗證代碼已被正確掃描:
其他產品都貼有二維條碼QR碼格式,它可以使用任何的ISO8859-1字符,並可以編碼字符串,最多2,953個字符:
這將是方便的庫存跟蹤係統能夠存儲UPC-A條碼作為三個整數的元組,和QR代碼的條形碼的任何長度的字符串。
在Swift中可以使用一個枚舉來定義兩種類型的產品條形碼,結構可以是這樣的:
1 2 3 4 |
enum Barcode { case UPCA(Int, Int, Int) case QRCode(String) } |
這可以被理解為:
“定義一個名為條形碼枚舉類型,它可以是UPC-A的任一值類型的關聯值(Int,Int,Int),或QRCode的一個類型為String的關聯值。”
這個定義不提供任何實際的Int或String值,它隻是定義了條形碼常量和變量當等於Barcode.UPCA或Barcode.QRCode關聯值的類型的時候的存儲形式。
然後可以使用任何一種類型來創建新的條碼:
1
|
var productBarcode = Barcode.UPCA(8, 85909_51226, 3) |
此示例創建一個名為productBarcode新的變量,並與相關聯的元組值賦給它Barcode.UPCA的值(8,8590951226,3)。提供的“標識符”值都有整數加下劃線的文字,85909_51226,使其更易於閱讀的條形碼。
同一產品可以分配不同類型的條形碼:
1
|
productBarcode = .QRCode("ABCDEFGHIJKLMNOP") |
在這一點上,原來Barcode.UPCA和其整數值被新的Barcode.QRCode及其字符串值代替。_條形碼的常量和變量可以存儲任何一個_UPCA或QRCode的(連同其關聯值),但它們隻能存儲其中之一在任何指定時間。
不同的條碼類型像以前一樣可以使用一個switch語句來檢查,但是這一次相關的值可以被提取作為switch語句的一部分。您提取每個相關值作為常數(let前綴)或變量(var前綴)不同的情況下,在switch語句的case代碼內使用:
1 2 3 4 5 6 7 |
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).") } // prints "QR code with value of ABCDEFGHIJKLMNOP." |
如果所有的枚舉成員的關聯值的提取為常數,或者當所有被提取為變量,為了簡潔起見,可以放置一個var,或let標注在成員名稱前:
1 2 3 4 5 6 7 |
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).") } // prints "QR code with value of ABCDEFGHIJKLMNOP." |
4、原始值
在關聯值的條形碼的例子演示了一個枚舉的成員如何能聲明它們存儲不同類型的關聯值。作為替代關聯值,枚舉成員可以拿出預先填入缺省值(稱為原始值),從而具有相同的類型。
這裏是一個存儲原始的ASCII值命名枚舉成員的一個例子:
1 2 3 4 5 |
enum ASCIIControlCharacter: Character { case Tab = "\t" case LineFeed = "\n" case CarriageReturn = "\r" } |
在這裏,原始值被定義為字符類型的枚舉叫做ASCIIControlCharacter,並設置了一些比較常見的ASCII控製字符。字符值的字符串和字符的描述。
注意,原始值是不相同關聯值。原始值設置為預填充的值時,應先在你的代碼中定義枚舉,像上述三個ASCII碼。對於一個特定的枚舉成員的原始值始終是相同的。當你創建一個基於枚舉的常量或變量的新成員的關聯值設置,每次當你這樣做的時候可以是不同的。
原始值可以是字符串,字符,或任何整數或浮點數類型。每個原始值必須在它的枚舉中唯一聲明。當整數被用於原始值,如果其他枚舉成員沒有值時,它們自動遞增。
下麵列舉的是一個細化的早期Planet枚舉,使用原始整數值來表示每個Planet的太陽係的順序:
1 2 3 |
enum Planet: Int { case Mercury = 1, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune } |
自動遞增意味著Planet.Venus具有2的原始值,依此類推。
訪問其toRaw方法枚舉成員的原始值:
1 2 |
let earthsOrder = Planet.Earth.toRaw() // earthsOrder is 3 |
使用枚舉的fromRaw方法來試圖找到一個特定的原始值枚舉成員。這個例子識別Uranus的位置通過原始值為7:
1 2 |
let possiblePlanet = Planet.fromRaw(7) // possiblePlanet is of type Planet? and equals Planet.Uranus |
然而,並非所有可能的Int值都會找到一個匹配的星球。正因如此,該fromRaw方法返回一個可選的枚舉成員。在上麵的例子中,是possiblePlanet類型Planet?或“可選的Planet”。
如果你試圖找到一個Planet為9的位置,通過fromRaw返回可選的Planet值將是無:
1 2 3 4 5 6 7 8 9 10 11 12 |
let positionToFind = 9 if 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)") } // prints "There isn't a planet at position 9" |
這個範例使用somePlanet= Planet.fromRaw(9)來嚐試訪問可選集合Planet,在可選Planet集合中設置檢索條件somePlanet,在原始值為9的情況下,不能檢索到位置為9的星球,所有else分支被執行。
最後更新:2017-04-03 07:56:58