級別: ★☆☆☆☆
標簽:「iOS」「Swift 5.1」「枚舉」「迭代枚舉」「枚舉關聯值」「遞歸枚舉」
作者: 沐靈洛
審校: QiShare團隊
Enumeration:枚舉類型
一個枚舉類型是為一組相關聯的值定義的一個公共類型,使得這些關聯值能夠在代碼中以類型安全的方式進行處理。
C語言中的枚舉類型將相關的枚舉項使用整數值表示。而Swift中的枚舉更靈活,并且沒有為枚舉項提供值。如果為為枚舉項提供值(則該值稱為原始值raw value
),該值可以是字符串,字符,任何整數或浮點類型的值。
另外,枚舉項可以指定任何類型的關聯值與枚舉項的值一起存儲。我們可以定義一組通用的相關項作為枚舉的一部分,每個枚舉都有一組比較合適的類型的不同值與之關聯。同時Swift中的枚舉類型采用了許多只有類才支持的特征,比如枚舉:
- 能夠提供計算屬性,用于支持枚舉當前值之外的額外的信息;
- 能夠提供實例方法,用于支持與枚舉所代表的值相關的功能;
- 能夠定義初始化方法,提供初始枚舉項的值;
- 能夠支持擴展, 可以在原始實現的基礎上擴展其功能;
- 能夠遵守協議,用于提供一切標準的功能;
枚舉語法
使用enum關鍵字引入枚舉,并將其整個定義放在{}
中
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
,可以使枚舉,擁有一個關于所有枚舉項的集合。調用枚舉類型的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)
}
枚舉關聯值
枚舉類型中在枚舉項的旁邊存貯額外的其他類型的值,這些值被稱為關聯值。并且每次在代碼中使用有關聯值的枚舉項時,該關聯值都會有所不同。
Swift中可以定義枚舉以存儲任何給定類型的關聯值,并且每個枚舉項的值類型也可以不同。
例如:商品碼有二維碼和條形碼兩種形式。二維碼的信息可以提取為String
類型,條形碼的信息是一串數字,按照:一位系統數字+五位制造商標識+五位產品標識+一位校驗數字的格式,我們可以聯想到(Int,Int,Int,Int)
元組類型。所以我們使用關聯值定義枚舉類型如下: 通用商品條碼
enum ProductCode {
case upc(Int,Int,Int,Int)//通用商品條碼
case qrCode(String)//二維碼
}
上述代碼解讀:定義了一個枚舉類型ProductCode
,有兩個枚舉項upc
和qrCode
。這兩個枚舉項分別可以存儲(Int, Int, Int, Int)
類型和String
類型的關聯值。
ProductCode
枚舉類型的定義不提供任何實際意義的Int
或String
值。 它只定義ProductCode
常量和變量在等于ProductCode.upc
或ProductCode.qrCode
時可以存儲的關聯值的類型。
var productCode = ProductCode.upc(1, 22, 333, 4444)
productCode = ProductCode.qrCode("二維碼哈")
使用Switch
語句匹配枚舉項并且提取枚舉項的關聯值??梢允褂?code>let和var
將每個關聯值提取為常量或變量,以在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??梢允褂?code>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
枚舉類型的每一項都設置有相應的關聯值,并且addition
和multiplication
都關聯了可以存儲(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 給UILabel添加點擊事件
用SwiftUI給視圖添加動畫
用SwiftUI寫一個簡單頁面
Swift 5.1 (7) - 閉包
iOS App啟動優化(三)—— 自己做一個工具監控App的啟動耗時
iOS App啟動優化(二)—— 使用“Time Profiler”工具監控App的啟動耗時
iOS App啟動優化(一)—— 了解App的啟動流程