Swift 5.1 (8) - 枚舉類型

級別: ★☆☆☆☆
標籤:「iOS」「Swift 5.1」「枚舉」「迭代枚舉」「枚舉關聯值」「遞歸枚舉」
做者: 沐靈洛
審校: QiShare團隊php


Enumeration:枚舉類型

一個枚舉類型是爲一組相關聯的值定義的一個公共類型,使得這些關聯值可以在代碼中以類型安全的方式進行處理。 C語言中的枚舉類型將相關的枚舉項使用整數值表示。而Swift中的枚舉更靈活,而且沒有爲枚舉項提供值。若是爲爲枚舉項提供值(則該值稱爲原始值raw value),該值能夠是字符串,字符,任何整數或浮點類型的值。 另外,枚舉項能夠指定任何類型的關聯值與枚舉項的值一塊兒存儲。咱們能夠定義一組通用的相關項做爲枚舉的一部分,每一個枚舉都有一組比較合適的類型的不一樣值與之關聯。同時Swift中的枚舉類型採用了許多隻有類才支持的特徵,好比枚舉:git

  • 可以提供計算屬性,用於支持枚舉當前值以外的額外的信息;
  • 可以提供實例方法,用於支持與枚舉所表明的值相關的功能;
  • 可以定義初始化方法,提供初始枚舉項的值;
  • 可以支持擴展, 能夠在原始實現的基礎上擴展其功能;
  • 可以遵照協議,用於提供一切標準的功能;

枚舉語法

使用enum關鍵字引入枚舉,並將其整個定義放在{}github

enum <#name#> {
    case <#case#>
}
複製代碼

枚舉的定義與使用express

enum CompassPoint {
    case north
    case west
    case east
    case south
}
//多個枚舉寫在一行中。
enum CompassPoint {
    case north,west,east,south
}
複製代碼

每一個枚舉定義都定義了一個新類型。編程

var direction : CompassPoint = .north
var direction = CompassPoint.north
複製代碼

使用Switch語句匹配枚舉值

let direction : CompassPoint = .north
switch direction {
case .east:
    print("ease")
case .north:
    print("north")
case .west:
    print("west")
case .south:
    print("south")
}
複製代碼

迭代枚舉項

經過在枚舉名稱後使用: CaseIterable,可使枚舉,擁有一個關於全部枚舉項的集合。調用枚舉類型的allCases屬性,能夠獲取這個集合。swift

enum CompassPoint : CaseIterable{
    case north,west,east,south
}
//輸出allCases
/*
 log:[swift_basic.EnumerationPractice.CompassPoint.north, swift_basic.EnumerationPractice.CompassPoint.west, swift_basic.EnumerationPractice.CompassPoint.east, swift_basic.EnumerationPractice.CompassPoint.south]
 */
print(CompassPoint.allCases)
switch CompassPoint.allCases.first! {
case .north:
    print("輸出正確")
default:
    print("輸出失敗")
}
//eachCase每一項都是枚舉類型的示例
for eachCase in CompassPoint.allCases {
    /*north
     west
     east
     south*/
    print(eachCase)
}
複製代碼

枚舉關聯值

枚舉類型中在枚舉項的旁邊存貯額外的其餘類型的值,這些值被稱爲關聯值。而且每次在代碼中使用有關聯值的枚舉項時,該關聯值都會有所不一樣。 Swift中能夠定義枚舉以存儲任何給定類型的關聯值,而且每一個枚舉項的值類型也能夠不一樣。 例如:商品碼有二維碼和條形碼兩種形式。二維碼的信息能夠提取爲String類型,條形碼的信息是一串數字,按照:一位系統數字+五位製造商標識+五位產品標識+一位校驗數字的格式,咱們能夠聯想到(Int,Int,Int,Int)元組類型。因此咱們使用關聯值定義枚舉類型以下: 通用商品條碼安全

enum ProductCode {
    case upc(Int,Int,Int,Int)//通用商品條碼
    case qrCode(String)//二維碼
}
複製代碼

上述代碼解讀:定義了一個枚舉類型ProductCode,有兩個枚舉項upcqrCode。這兩個枚舉項分別能夠存儲(Int, Int, Int, Int)類型和String類型的關聯值。 ProductCode枚舉類型的定義不提供任何實際意義的IntString值。 它只定義ProductCode常量和變量在等於ProductCode.upcProductCode.qrCode時能夠存儲的關聯值的類型。bash

var productCode = ProductCode.upc(1, 22, 333, 4444)
productCode = ProductCode.qrCode("二維碼哈")
複製代碼

使用Switch語句匹配枚舉項而且提取枚舉項的關聯值。可使用letvar將每一個關聯值提取爲常量或變量,以在case的正文中使用。微信

var productCode = ProductCode.upc(1, 22, 333, 4444)
switch productCode {
case .upc(let x, let y, let z, let zz):
    print("從switch語句中提取的相匹配的枚舉項的關聯值爲x:\(x) y:\(y) z:\(z) zz:\(zz)")
case ProductCode.qrCode(let str):
    print("從switch語句中提取的相匹配的枚舉項的關聯值爲:\(str)")
}
productCode = ProductCode.qrCode("二維碼哈")
switch productCode {
case let .upc(x, y, z, zz):
    print("從switch語句中提取的相匹配的枚舉項的關聯值爲x:\(x) y:\(y) z:\(z) zz:\(zz)")
case let .qrCode(str):
    print("從switch語句中提取的相匹配的枚舉項的關聯值爲:\(str)")
}
複製代碼

枚舉原始值

關聯值的示例中展現了枚舉如何聲明它們存儲不一樣類型的關聯值。做爲關聯值的替代,枚舉也能夠預先填充默認值(稱爲原始值),它們都是相同的類型。閉包

enum NameType : String{
    case ZhangFei = "張飛"
    case GuanYu = "關羽"
    case LiuBei = "劉備"
}
let name = NameType.ZhangFei
print(name.rawValue)//!< log:張飛
複製代碼

枚舉NameType的原始值被定義爲String類型,並被設置原始值。原始值能夠是字符串,字符或任何整數或浮點數類型。每一個原始值在其枚舉聲明中必須是惟一的。 原始值與關聯值不一樣。原始值:預先填充的默認值,對於特定的枚舉項,其原始值老是相同的。關聯值:每次基於枚舉狀況之一建立一個常量或變量時,其關聯值能夠是不一樣的。

隱式分配原始值

使用原始值爲整數或字符串類型的枚舉時,能夠不用爲每一個case顯式分配原始值。由於Swift會自動爲咱們分配值。例如,當整數用於原始值時,每種狀況的隱含值比前一種狀況多一個。若是第一種狀況沒有設置值,則其值爲0。可使用rawValue屬性訪問枚舉項的原始值。

enum Planet: Int,CaseIterable {
    case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
}
//查看各項的`rawvalue`
var planetOutStr = "枚舉類型Planet各項的原始值"
for item in Planet.allCases {
    planetOutStr += "\(item) : \(item.rawValue) "
}
print(planetOutStr)//!< 枚舉類型Planet各項的原始值mercury : 1 venus : 2 earth : 3 mars : 4 jupiter : 5 saturn : 6 uranus : 7 neptune : 8
複製代碼

原始值爲字符串類型的枚舉,每一個枚舉項隱式分配的原始值是該枚舉項的名稱。

enum CompassPoint :String,CaseIterable{
    case north,west,east,south
}
var directionOutStr = "枚舉類型CompassPoint各項的原始值"
for item in CompassPoint.allCases {
    directionOutStr += "\(item) : '\(item.rawValue)' "
}
print(directionOutStr)//!< 枚舉類型CompassPoint各項的原始值north : 'north' west : 'west' east : 'east' south : 'south' 
複製代碼

從原始值初始化

若是使用原始值類型定義枚舉,則枚舉會自動接收一個參數名:rawValue參數類型:原始值類型的初始話方法,並返回枚舉項或nil。咱們可使用此初始化方法嘗試建立該枚舉類型的新實例。

let possiblePalnet = Planet.init(rawValue: 7)
//!< Planet類型的可選項。Optional(swift_basic.EnumerationPractice.Planet.uranus)
//沒法根據原始值匹配到枚舉項的狀況
let possiblePalnet = Planet.init(rawValue: 9)//!< Planet類型的可選項。9已經超出了枚舉的全部項的範圍。log:nil
//須要解包,可選綁定
if let possiblePlanet = possiblePalnet {
    print(possiblePlanet)
    switch possiblePlanet {
    case .earth:
        print("地球")
    default:
        print("其餘行星")
    }
} else {
    print("沒有找到合適的枚舉項")
}
複製代碼

枚舉的遞歸

定義一個枚舉時,若該枚舉類型的某個case關聯了一個或多個該枚舉類型的值時,系統會自動提示,須要添加indirect關鍵字,由於此時的枚舉類型已經被定義爲了擁有遞歸結構的遞歸枚舉。 經過在枚舉項的前面使用indirect關鍵字,來表示此枚舉項是可遞歸的。 如下示例爲存儲了三個數學表達式遞歸枚舉的定義。

//方式一
//遞歸枚舉,存儲了三個數學表達式 使用`indirect`來表示枚舉項時可遞歸調用的。
enum ArithmeticExpression {
    case number(Int)
    indirect case addition(ArithmeticExpression, ArithmeticExpression)
    indirect case multiplication(ArithmeticExpression, ArithmeticExpression)
}
複製代碼

在枚舉開始以前使用indirect關鍵字,爲具備關聯值的全部枚舉項啓用遞歸:

//方式二
indirect enum ArithmeticExpression {
    case number(Int)
    case addition(ArithmeticExpression, ArithmeticExpression)
    case multiplication(ArithmeticExpression, ArithmeticExpression)
}
複製代碼

ArithmeticExpression枚舉類型的每一項都設置有相應的關聯值,而且additionmultiplication都關聯了能夠存儲(ArithmeticExpression, ArithmeticExpression)類型的值。即:分別關聯了兩個ArithmeticExpression枚舉類型。

遞歸枚舉的嵌套:

//存儲了一個爲5的關聯值
let five = ArithmeticExpression.number(5)
//存儲了一個爲6的關聯值
let six = ArithmeticExpression.number(6)
//存儲了一個爲3的關聯值
let three = ArithmeticExpression.number(3)
//addition枚舉項,關聯了兩個當前枚舉類型的值。
let sum = ArithmeticExpression.addition(five, six)
//multiplication枚舉項,關聯了兩個當前枚舉類型的值。
let multiplication = ArithmeticExpression.multiplication(sum, three)//數學表達式的呈現形式。
複製代碼

建立遞歸函數驗證multiplication是否能夠計算正確結果

//建立一個遞歸函數驗證可遞歸性
func arithmeticOperation(_ expression : ArithmeticExpression) -> Int {
    
    switch expression {
    case let .number(x):
        return x
    case let .addition(x, y):
        //! 此處x和y仍舊是表達式,因此須要先進行遞歸運算得出`Int`類型的關聯值
        return arithmeticOperation(x) + arithmeticOperation(y)
    case .multiplication(let x, let y):
        //! 此處x和y仍舊是`ArithmeticExpression`,因此須要先進行遞歸運算得出`Int`類型的關聯值
        return arithmeticOperation(x) * arithmeticOperation(y)
    }
}
print("嵌套的數學表達式進行遞歸調用後的結果是:\(arithmeticOperation(multiplication))")
//!< log:嵌套的數學表達式進行遞歸調用後的結果是:33

複製代碼

參考資料: swift 5.1官方編程指南


瞭解更多iOS及相關新技術,請關注咱們的公衆號:

小編微信:可加並拉入《QiShare技術交流羣》。

關注咱們的途徑有:
QiShare(簡書)
QiShare(掘金)
QiShare(知乎)
QiShare(GitHub)
QiShare(CocoaChina)
QiShare(StackOverflow)
QiShare(微信公衆號)

推薦文章:
iOS 給UILabel添加點擊事件
用SwiftUI給視圖添加動畫
用SwiftUI寫一個簡單頁面
Swift 5.1 (7) - 閉包
iOS App啓動優化(三)—— 本身作一個工具監控App的啓動耗時
iOS App啓動優化(二)—— 使用「Time Profiler」工具監控App的啓動耗時
iOS App啓動優化(一)—— 瞭解App的啓動流程
奇舞週刊

相關文章
相關標籤/搜索