級別: ★☆☆☆☆
標(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,有兩個枚舉項upc和qrCode。這兩個枚舉項分別可以存儲(Int, Int, Int, Int)類型和String類型的關(guān)聯(lián)值。
ProductCode枚舉類型的定義不提供任何實際意義的Int或String值。 它只定義ProductCode常量和變量在等于ProductCode.upc或ProductCode.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)值,并且addition和multiplication都關(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的啟動流程