Swift 5.1 (8) - 枚舉類型

級別: ★☆☆☆☆
標(biāo)簽:「iOS」「Swift 5.1」「枚舉」「迭代枚舉」「枚舉關(guān)聯(lián)值」「遞歸枚舉」
作者: 沐靈洛
審校: QiShare團(tuán)隊


Enumeration:枚舉類型

一個枚舉類型是為一組相關(guān)聯(lián)的值定義的一個公共類型,使得這些關(guān)聯(lián)值能夠在代碼中以類型安全的方式進(jìn)行處理。
C語言中的枚舉類型將相關(guān)的枚舉項使用整數(shù)值表示。而Swift中的枚舉更靈活,并且沒有為枚舉項提供值。如果為為枚舉項提供值(則該值稱為原始值raw value),該值可以是字符串,字符,任何整數(shù)或浮點類型的值。
另外,枚舉項可以指定任何類型的關(guān)聯(lián)值與枚舉項的值一起存儲。我們可以定義一組通用的相關(guān)項作為枚舉的一部分,每個枚舉都有一組比較合適的類型的不同值與之關(guān)聯(lián)。同時Swift中的枚舉類型采用了許多只有類才支持的特征,比如枚舉:

  • 能夠提供計算屬性,用于支持枚舉當(dāng)前值之外的額外的信息;
  • 能夠提供實例方法,用于支持與枚舉所代表的值相關(guān)的功能;
  • 能夠定義初始化方法,提供初始枚舉項的值;
  • 能夠支持?jǐn)U展, 可以在原始實現(xiàn)的基礎(chǔ)上擴展其功能;
  • 能夠遵守協(xié)議,用于提供一切標(biāo)準(zhǔn)的功能;

枚舉語法

使用enum關(guān)鍵字引入枚舉,并將其整個定義放在{}

enum <#name#> {
    case <#case#>
}

枚舉的定義與使用

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,可以使枚舉,擁有一個關(guān)于所有枚舉項的集合。調(diào)用枚舉類型的allCases屬性,可以獲取這個集合。

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)
}

枚舉關(guān)聯(lián)值

枚舉類型中在枚舉項的旁邊存貯額外的其他類型的值,這些值被稱為關(guān)聯(lián)值。并且每次在代碼中使用有關(guān)聯(lián)值的枚舉項時,該關(guān)聯(lián)值都會有所不同。
Swift中可以定義枚舉以存儲任何給定類型的關(guān)聯(lián)值,并且每個枚舉項的值類型也可以不同。
例如:商品碼有二維碼和條形碼兩種形式。二維碼的信息可以提取為String類型,條形碼的信息是一串?dāng)?shù)字,按照:一位系統(tǒng)數(shù)字+五位制造商標(biāo)識+五位產(chǎn)品標(biāo)識+一位校驗數(shù)字的格式,我們可以聯(lián)想到(Int,Int,Int,Int)元組類型。所以我們使用關(guān)聯(lián)值定義枚舉類型如下: 通用商品條碼

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

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

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

使用Switch語句匹配枚舉項并且提取枚舉項的關(guān)聯(lián)值??梢允褂?code>let和var將每個關(guān)聯(lián)值提取為常量或變量,以在case的正文中使用。

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

枚舉原始值

關(guān)聯(lián)值的示例中展示了枚舉如何聲明它們存儲不同類型的關(guān)聯(lián)值。作為關(guān)聯(lián)值的替代,枚舉也可以預(yù)先填充默認(rèn)值(稱為原始值),它們都是相同的類型。

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

枚舉NameType的原始值被定義為String類型,并被設(shè)置原始值。原始值可以是字符串,字符或任何整數(shù)或浮點數(shù)類型。每個原始值在其枚舉聲明中必須是唯一的。
原始值與關(guān)聯(lián)值不同。原始值:預(yù)先填充的默認(rèn)值,對于特定的枚舉項,其原始值總是相同的。關(guān)聯(lián)值:每次基于枚舉情況之一創(chuàng)建一個常量或變量時,其關(guān)聯(lián)值可以是不同的。
隱式分配原始值
使用原始值為整數(shù)或字符串類型的枚舉時,可以不用為每個case顯式分配原始值。因為Swift會自動為我們分配值。例如,當(dāng)整數(shù)用于原始值時,每種情況的隱含值比前一種情況多一個。如果第一種情況沒有設(shè)置值,則其值為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' 

從原始值初始化
如果使用原始值類型定義枚舉,則枚舉會自動接收一個參數(shù)名:rawValue參數(shù)類型:原始值類型的初始話方法,并返回枚舉項或nil。我們可以使用此初始化方法嘗試創(chuàng)建該枚舉類型的新實例。

let possiblePalnet = Planet.init(rawValue: 7)
//!< Planet類型的可選項。Optional(swift_basic.EnumerationPractice.Planet.uranus)
//無法根據(jù)原始值匹配到枚舉項的情況
let possiblePalnet = Planet.init(rawValue: 9)//!< Planet類型的可選項。9已經(jīng)超出了枚舉的所有項的范圍。log:nil
//需要解包,可選綁定
if let possiblePlanet = possiblePalnet {
    print(possiblePlanet)
    switch possiblePlanet {
    case .earth:
        print("地球")
    default:
        print("其他行星")
    }
} else {
    print("沒有找到合適的枚舉項")
}

枚舉的遞歸

定義一個枚舉時,若該枚舉類型的某個case關(guān)聯(lián)了一個或多個該枚舉類型的值時,系統(tǒng)會自動提示,需要添加indirect關(guān)鍵字,因為此時的枚舉類型已經(jīng)被定義為了擁有遞歸結(jié)構(gòu)的遞歸枚舉。通過在枚舉項的前面使用indirect關(guān)鍵字,來表示此枚舉項是可遞歸的。
以下示例為存儲了三個數(shù)學(xué)表達(dá)式遞歸枚舉的定義。

//方式一
//遞歸枚舉,存儲了三個數(shù)學(xué)表達(dá)式 使用`indirect`來表示枚舉項時可遞歸調(diào)用的。
enum ArithmeticExpression {
    case number(Int)
    indirect case addition(ArithmeticExpression, ArithmeticExpression)
    indirect case multiplication(ArithmeticExpression, ArithmeticExpression)
}

在枚舉開始之前使用indirect關(guān)鍵字,為具有關(guān)聯(lián)值的所有枚舉項啟用遞歸:

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

ArithmeticExpression枚舉類型的每一項都設(shè)置有相應(yīng)的關(guān)聯(lián)值,并且additionmultiplication都關(guān)聯(lián)了可以存儲(ArithmeticExpression, ArithmeticExpression)類型的值。即:分別關(guān)聯(lián)了兩個ArithmeticExpression枚舉類型。

遞歸枚舉的嵌套:

//存儲了一個為5的關(guān)聯(lián)值
let five = ArithmeticExpression.number(5)
//存儲了一個為6的關(guān)聯(lián)值
let six = ArithmeticExpression.number(6)
//存儲了一個為3的關(guān)聯(lián)值
let three = ArithmeticExpression.number(3)
//addition枚舉項,關(guān)聯(lián)了兩個當(dāng)前枚舉類型的值。
let sum = ArithmeticExpression.addition(five, six)
//multiplication枚舉項,關(guān)聯(lián)了兩個當(dāng)前枚舉類型的值。
let multiplication = ArithmeticExpression.multiplication(sum, three)//數(shù)學(xué)表達(dá)式的呈現(xiàn)形式。

創(chuàng)建遞歸函數(shù)驗證multiplication是否可以計算正確結(jié)果

//創(chuàng)建一個遞歸函數(shù)驗證可遞歸性
func arithmeticOperation(_ expression : ArithmeticExpression) -> Int {
    
    switch expression {
    case let .number(x):
        return x
    case let .addition(x, y):
        //! 此處x和y仍舊是表達(dá)式,所以需要先進(jìn)行遞歸運算得出`Int`類型的關(guān)聯(lián)值
        return arithmeticOperation(x) + arithmeticOperation(y)
    case .multiplication(let x, let y):
        //! 此處x和y仍舊是`ArithmeticExpression`,所以需要先進(jìn)行遞歸運算得出`Int`類型的關(guān)聯(lián)值
        return arithmeticOperation(x) * arithmeticOperation(y)
    }
}
print("嵌套的數(shù)學(xué)表達(dá)式進(jìn)行遞歸調(diào)用后的結(jié)果是:\(arithmeticOperation(multiplication))")
//!< log:嵌套的數(shù)學(xué)表達(dá)式進(jìn)行遞歸調(diào)用后的結(jié)果是:33

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


推薦文章:
iOS 給UILabel添加點擊事件
用SwiftUI給視圖添加動畫
用SwiftUI寫一個簡單頁面
Swift 5.1 (7) - 閉包
iOS App啟動優(yōu)化(三)—— 自己做一個工具監(jiān)控App的啟動耗時
iOS App啟動優(yōu)化(二)—— 使用“Time Profiler”工具監(jiān)控App的啟動耗時
iOS App啟動優(yōu)化(一)—— 了解App的啟動流程

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容