enum 定義了一組相關值的通用類型,並讓你能夠在代碼中以類型安全的方式處理這些值。
enum 在 Swift 語言中相較於其他語言更加靈活了,而且他不必為 enum 中每種 case 都提供值。如果我們為 enum 每個 case 都提供值,則該值可以是String,Character 或任何 Int 或是 Float-point 類型的值。
而且,枚舉中的情況可以指定每個不同情況下的值儲存任何類型的關聯值。你可以定義一組有關聯 case 的合集作為 enum 的一部分,每個 enum 有著一組不同合適類型的合集值。
Swift 中的枚舉是具有自己權限的 first-class 類型,他們採用許多慣例上只被 class 中的支援的方式,像是計算屬性提供有關 enum 目前值的附加訊息,而且實例方法提供提供 enum 表示值有關聯的功能。枚舉也可以定義初始器來提供初始 case 中的值;並可以擴大到超出原本能夠實現範圍的功能,並且符合協議以提供標準功能。
你可以透過下列方式定義一個 enum
enum EnumerationName { // 輸入 enum 名稱
//在此定義 enum 的 case
}
在此我們建立一個方向的枚舉 (上下左右) :
enum Direction {
case up
case down
case left
case right
}
你也可以在一個 case 設多個值,每個值必須由逗號 (,) 分開:
enum Letter {
case A, B, C, D, E
case F, G, H, I, J
}
你當然也可以宣告 Direction 中的 case 給一個變數,當你與 Direction 中可用的某一值一同初始化時 direction1 的類型會被推斷出來。之後如果需要改變 direction1 中的值,只需在後面加上一個點 (.) 就會跳出 Direction 中的 case 。(因為 direction1 的類型是已知的)
你可以用 switch 語句來匹配每一個單獨的 enum 值:
判斷 direction1 中的值,如果為 .up 則印出 ⬆️ , .down 則印出 ⬇️ ...以此類推。
如果你無法為所有的情況設一個 case , 你也可以用 default 來包含其他無法預設的情況:
你可以定義 Swift 中的枚舉來儲存任何你自定類型的關聯值,如果你需要值的類型可以根據每種不同的 case 做改變。下面我們舉一個商品編碼,可以使用不同的 code 來呈現:
enum Code {
case numCode(Int, Int, Int, Int) //由四個 Int 值所組成
case nameCode(String,Int) //由一個 String 及一個 Int 值組成
}
之後我們可以把他宣告到一個變數中:
var productCode = Code.nameCode("蘋果", 20)
這個 productCode 變數被賦予了一個 Code.nameCode 中的值 關聯了值為 ("蘋果", 20) 的元組
同樣的產品可以被分配一個不同類型的 Code,宣告之後我們也可以更改他的內容:
productCode = .numCode(830, 775, 08, 20)
接下來我們可以用 switch 來判斷現在產品的 Code 為哪種格式,並將他的內容印出, 我們可以對 case 中的對應值為他們宣告成一個常數或變數,讓他可以在 switch 中的 case 中被使用:
如果一個 case 中都是被宣告成常數或者是變數的話,我們可以在 case 的名稱前面加上 let 或是 var 即可,使得程式碼更簡潔。
上面的關聯值的範例說明了 enum 中的 case 是如何聲明它們存儲不同類型的相關值的。作為相關值的另一種選擇,enum 中的 case 也可以用相同類型的默認值預先輸入。
enum numEnglish:Int {
case one = 1
case two = 2
case three = 3
}
當你使用 enum 來儲存整數或是字串作為的原始值時,你不必明確的分配一個原始值給每個 case ,當你不這麼做時,Swift 將會自動幫你分配值。
舉個例子,當你使用整數當原始值時,每個 case 的隱含值都比前一個 case 大一,如果第一個情況沒有設定值,那麼他的值就為 0 。
我們使用英文字母順序來作為例子:
enum AlphabeticalOrder:Int {
case A = 1, B , C , D ,F ,G
}
那麼 A 有一個明確的原始值為 1 ,那麼 B 的隱含原始值就為 2 , C 為 3 ...以此類推。
當字符串被用於原始值,那麼每一個成員的隱式原始值則是那個成員的名稱,我們使用前面提過的方向作為使用範例。
enum Direction:String {
case up, down, left, right
}
這個例子中 Direction.up 的隱含原始值為 up, .down 的隱含原始值為 down。...以此類推
之後你可以透過 rawValue 來查看 enum 中的 case 的原始值:
如果你定義了一個使用原始值的 enum 類型,那麼枚舉就會自動收到一個可以接受原始值類型的值的初始化器(叫做 rawValue的形式參數)然後返回一個枚舉成員或者 nil,你可以使用這個初始化器來嘗試創建一個 enum 的新實例。
我們可以從它的原始值 5 來辨認出字母 F :
總之,不是所有可能的值都會對應到一個 case 。因此原始值的初始化器總是返回可選的枚舉成員。因此我們 alphabeticalOrder 的類型是 alphabeticalOrder ,或者是 Optional alphabeticalOrder。
原始值初始化器是一個可失敗初始化器,因為不是所有原始值都將返回一個 enum 的 case
如果你嘗試尋找超出範圍的字母,那麼被原始值初始化器返回的 Optional alphabeticalOrder 值將會是 nil:
遞歸枚舉是擁有另一個枚舉作為枚舉 case 關聯值的枚舉。當編譯器操作遞歸枚舉時必須插入間接尋址層。你可以在聲明枚舉 case 之前使用indirect 關鍵字來明確表示它是遞歸的。我們使用一個加減運算來當範例:
你可以再 case 之前加上一個 indirect 表示他是遞歸的
enum Calculation {
case number(Int)
indirect case add(Calculation,Calculation)
indirect case sub(Calculation,Calculation)
}
或者你可以將 indirect 寫在 enum 前面表示整個 case 都是可以遞歸的
indirect enum Calculation {
case number(Int)
case add(Calculation,Calculation)
case sub(Calculation,Calculation)
}
在上面的例子中,我們儲存三種數學運算表達式:單一的數字,有兩個兩個表達式的加法,以及有兩個表達式的乘法。我們兩個 case (add 和 mul) 擁有同樣是數學表達式的關聯值——這些關聯值讓嵌套表達式成為可能。由於數據是內嵌的,用來儲存數據的枚舉同樣需要支持內嵌——這就是說枚舉需要遞歸。下邊的程式碼創建了 ( 5 + 2 ) * 2 遞歸枚舉:
let five = Calculation.number(5)
let two = Calculation.number(2)
let sum = Calculation.add(five , two)
let product = Calculation.mul(sum, Calculation.number(2))
遞歸函數是一種操作遞歸結構數據的簡單方法。
這個函數通過直接返回關聯值來判斷普通數字。它通過衡量表達式 a 和 b 判斷是加法還是乘法,然後對它們加或者乘。
剛入門的時候,常常好奇為什麼 A 方法不能用另外一種 B 方法做,其中 enum 就是一個例子,因為當時學習的時候覺得他與 array 大同小異,但是使用起來才會發現它們的是有差別的,這邊我使用一個簡單的例子,建立方法不同,但內容是相同的資料。
這邊我們看得出來,想調用 array 中的 apple 值必須了解他的序在哪才能調用,但是 enum 中我們可以使用一個點 (.) 加上名稱 appple 就能調用它,不僅不用知道他的排序,還能直接使用名稱調用它。
而且假如我們今天要將它存入一個變數中,array 無法限制它的存取值,因為只要類型為 String 的他都能夠儲存。而使用 enum 時,我們可以將他限制在 appple,banana,cat 三種情況中。