枚舉入門
來苫幢,小明 給我說說什么是枚舉
小明: 你把手舉起來
然后呢碧信?
小明: 你看我的手 舉沒舉 ?
?
基本定義
枚舉作為 Swift 的 一等類型
跛溉, 我們正好可以通過枚舉焊切, 來 一一列舉 Swift 其它 強(qiáng)有力的 類型
首先寫出枚舉的 2種表達(dá)方式
它們被放在一個(gè)大括號(hào)里
,縱向排列芳室,互不干擾
enum SwiftType {
case protocol
case enum
case struct
case class
case tuple
case function
}
?
當(dāng)然它們也可以抱團(tuán)相擁在一起, 以 逗號(hào)
來互相敬畏
enum ProgrammingLanguage {
case protocol, enum, struct, class, tuple, function
}
掌握以上幾種類型蛛蒙,便可呼風(fēng)喚雨
那么我就以初學(xué)者的姿態(tài)記錄一下枚舉吧
?
原始值 rawValue
Swift 的枚舉 并不會(huì)像 OC 那樣固定的賦予一個(gè)默認(rèn)的整形值
,并沒有所謂的從上到下渤愁,原始值
也不是 從0到5
它們的原始值(rawValue)
也可以是其它類型
enum RawValueType {
case 整形
case 字符
case 字符串
case 浮點(diǎn)型
}
但是它們必須擁有一個(gè)共同的類型
且 原始值唯一
?
像 ProgrammingLanguage 這種沒有指明
原始值類型的枚舉 沒有原始值,
它們的實(shí)例 也沒法用點(diǎn)語法 點(diǎn)出 rawValue
?
賦予原始值類型
如果想像OC 一樣 拿到原始值牵祟,我們可以指定類型
enum ProgrammingLanguage: Int {
case Swift
case OC
case Python
case Java
}
一旦我們給定整形,也就意味著它們默認(rèn)是從0
開始的
?
下一個(gè)枚舉的原始值 = 上一個(gè)枚舉的原始值 + 1
?
??只有整形是這樣 依次累加哦
?
var p = ProgrammingLanguage.OC.rawValue
print(p)
// 打印 1抖格,因?yàn)?Swift 默認(rèn)是 0
現(xiàn)在 我們做一些改動(dòng)
enum ProgrammingLanguage: Int {
case Swift = 2
case OC
case Python = 6
case Java
}
var p = ProgrammingLanguage.OC.rawValue
print(p)
// 打印 3诺苹,因?yàn)?Swift 是 2
print(p1)
// 打印 7,因?yàn)?Python 是 6
?
字符串隱式原始值
如果我們指定枚舉類型 是 String
雹拄,那么其中的 case 對(duì)應(yīng)的rawValue收奔,就是它們的字符串化
enum Song: String {
case 夏日漱石
case 有暖氣
}
var s = Song.夏日漱石.rawValue
debugPrint(s)
// 打印 字符串 "夏日漱石"
?
rawValue 初始化
枚舉可以通過原始值 rawValue 來初始化
如
enum Drinking: String {
case cola
case sprite
case orangeJuice
}
var dg = Drinking(rawValue: "cola")
# 注意: 這里的dg 是可選型,因?yàn)?枚舉并不知道 你傳進(jìn)去的 rawValue 是否存在
# 下面會(huì)說到滓玖,這種方式的實(shí)例化 是一個(gè)可失敗的構(gòu)造器
?
Switch 匹配枚舉值
一般來說坪哄,我們寫出枚舉 是為了區(qū)分不同的case,和 OC 一樣 Switch
就成了我們 匹配枚舉值的 首選
Swift 的 枚舉情況 分為2種
// 登錄方式的枚舉
enum LoginWay {
case Apple
case QQ
case Wechat
case Weibo
}
let way = LoginWay.Apple
第一種: 窮盡所有
* 遍歷所有大括號(hào)內(nèi)的case势篡,一個(gè)不漏翩肌,不用寫defult
* 如果遺漏了,并且沒有defult 將會(huì)報(bào)錯(cuò)
switch way {
case .Apple:
print("apple")
case .QQ:
print("qq")
case .Wechat:
print("wechat")
case .Weibo:
print("weibo")
}
// 打印 "apple"
第二種: 投其所好
* 只展示部分我關(guān)心的case禁悠,其余的 用 defult 代表
* 不用展示全部
switch way {
case .QQ:
print("qq")
case .Weibo:
print("weibo")
default:
print("其它")
}
// 打印 "其它"
* 關(guān)聯(lián)值
什么是關(guān)聯(lián)值 念祭?
我們來看一個(gè)栗子
// 定義一個(gè)不同交通工具 上班時(shí)間 ,我們可以自由選擇上班方式
enum OnTheWayTime {
case bicycle(Int) // Int 類型關(guān)聯(lián)值 的 bicycle
case taxi(Int) // Int 類型關(guān)聯(lián)值 的 taxi
case bus(time: Int) // Int 類型關(guān)聯(lián)值 的 bus
case horse(String) // String 類型關(guān)聯(lián)值 的 horse
}
var t = OnTheWayTime.bicycle(60)
// "實(shí)例化一個(gè)變量"碍侦,并且給成員變量 bicycle 關(guān)聯(lián) Int值 60
如果我們想要 改變 t 粱坤,也就是我們的出行方式
在t 的類型已經(jīng)確定的情況下,我們可以不用帶枚舉名稱瓷产,直接 .
t = .taxi(30)
?
我們?nèi)?Switch 遍歷 這個(gè)枚舉站玄,看一下關(guān)聯(lián)值使用方式
switch t {
case .bicycle(let bic):
print("騎自行車上班要 \(bic) 分鐘")
case .taxi(let ti):
print("打出租車上班要 \(ti) 分鐘")
case .bus(time: let bs):
print("坐公交上班要 \(bs) 分鐘")
case .horse(let str):
print("騎馬要 \(str)")
}
可以提取關(guān)聯(lián)值 用 "let / var" 修飾
通過值綁定,生成的 "局部變量" 就與 "關(guān)聯(lián)值" 相連接
?
修改t ,再去遍歷濒旦,t 是可以任意變化的
t = .horse("很久")
打又昕酢:騎馬要很久
?
optional 關(guān)聯(lián)值
optional(可選型) 是比較常用的枚舉,它的成員值 .some
也是通過關(guān)聯(lián)值的方式
var age: Int?
age = 17
switch age {
case .none:
print("age 為 nil")
case .some(let value):
print("age 的值是: \(value)")
}
// 打影坦馈: age 的值是 17
?
問題
關(guān)聯(lián)值 的成員 有rawValue 嗎灾常?
答案是 沒有的
因?yàn)?rawValue 是遵從了 RawRepresentable
協(xié)議,協(xié)議中通過 associatedtype
來關(guān)聯(lián) rawValue, associatedtype 是用來定義 在協(xié)議中使用的 關(guān)聯(lián)類型
铃拇,雖然這個(gè)關(guān)聯(lián)類型是不確定的钞瀑,但是它們是統(tǒng)一的。
而 有關(guān)聯(lián)值的
枚舉慷荔,它們的類型是不統(tǒng)一
的雕什,所以無法使用 rawValue
?
結(jié)論
- 我們可以把關(guān)聯(lián)值當(dāng)做一個(gè)變量,
關(guān)聯(lián)之后的成員值 是可變的
- 關(guān)聯(lián)值 和 原始值不同显晶,
原始值的值 從開始 就是確定的贷岸,無法改變
? - 關(guān)聯(lián)值 無法使用 rawValue 屬性,因?yàn)樗鼈冾愋蜔o法 統(tǒng)一
?
枚舉的構(gòu)造過程
構(gòu)造過程
:保證新實(shí)例在第一次使用前完成正確的初始化
除了在上述中 提到 的 rawValue 初始化
,是一種隱藏了 init?
的可失敗的 構(gòu)造器之外磷雇,
我們還可以自定義 不隱藏的 init?
初始化器
enum Drinking: String {
case cola
case sprite
case orangeJuice
init?(str: String) {
switch str {
case "c":
self = .cola
case "s":
self = .sprite
case "o":
self = .orangeJuice
default:
return nil
}
}
}
下次2種方式都可以完成初始化:
let dg = Drinking(rawValue: "cola")
// print(dg!) cola
let gc = Drinking(str: "s")
// print(gc!) sprite
?
問題
我們不是列舉了所有的情況偿警,case "c","s"唯笙,"o"螟蒸,可以不用在init后面 加 問號(hào)嗎?可以不加defult 嗎崩掘?
答案是 不可以的
雖然 我們列舉的case 是 一一俱全的七嫌,但是我們并不能保證 初始化構(gòu)造的時(shí)候 你會(huì)傳入什么東西
,所以這個(gè)構(gòu)造是可能
會(huì)失敗的苞慢,結(jié)果是可選的诵原,所以就得加 ? 挽放,就需要defult來 處理不存在的 case
?
枚舉的屬性
計(jì)算屬性
來绍赛,看栗子
吃開封菜的時(shí)候到了
通過對(duì) kfc 的點(diǎn)單方式 單點(diǎn)/套餐 我們寫了一個(gè)枚舉,外界通過調(diào)用實(shí)例的 description 來獲得 描述
enum KFCFood {
case familyFood(Int)
case Other(String, String, String)
var description: String {
switch self {
case .familyFood(let num):
return "今天我一個(gè)人吃了 \(num) 個(gè)全家桶"
case let .Other(s1, s2, s3):
return "今天晚餐吃了\(s1) \(s2) 還有 \(s3)"
}
}
}
var k = KFCFood.familyFood(2)
print(k.description) // 今天我一個(gè)人吃了 2 個(gè)全家桶
k = .Other("漢堡", "可樂", "薯?xiàng)l")
print(k.description) // 今天晚餐吃了漢堡 可樂 還有 薯?xiàng)l
ps: 可樂 漢堡 和薯?xiàng)l 不也是套餐嗎 你個(gè)low 狗
我們定義了一個(gè) KFC 的枚舉
通過 關(guān)聯(lián)值 + 計(jì)算屬性
來 存儲(chǔ) 以及 獲得 description
?
小結(jié)
- 因?yàn)檫@里有關(guān)聯(lián)值辑畦,所以沒法通過 rawValue的方式 初始化惹资,也就是說如果通過關(guān)聯(lián)值初始化,就意味著 得到的實(shí)例 都是存在的航闺,
switch 里 不需要 defult
case let .Other(s1, s2, s3):
- 如果說關(guān)聯(lián)值得個(gè)數(shù) 不止一個(gè)褪测,那么我們使用的時(shí)候,可以把修飾 局部變量的
let/var 提到 最前面
?
擴(kuò)展 和協(xié)議 的第二種寫法
我們也可以 使用協(xié)議(Protocols)
和協(xié)議擴(kuò)展(Protocol Extension)
高大上 有沒有
通過協(xié)議 以及 協(xié)議擴(kuò)展可以更好的 將 成員值 與 屬性/方法 實(shí)現(xiàn)分離開
潦刃,
代碼也就自然而然的 通俗易懂了
enum KFCFood {
case familyFood(Int)
case Other(String, String, String)
}
protocol EatFood {
var description: String { get }
}
extension KFCFood: EatFood {
var description: String {
switch self {
case .familyFood(let num):
return "今天我一個(gè)人吃了 \(num) 個(gè)全家桶"
case let .Other(s1, s2, s3):
return "今天晚餐吃了\(s1) \(s2) 還有 \(s3)"
}
}
}
?
枚舉的方法
我們可以像在類中定義方法一樣侮措,在枚舉中我們也可以定義方法
enum Song: String {
case chinese
case english
func getName() -> String {
switch self {
case .chinese:
return "chinese"
case.english:
return "english"
}
}
}
let s = Song.chinese
print(s.getName())
// 打印 chinese
那么如果我們想在方法內(nèi)改變 自身的值呢?
比如 我們想中文歌 和 英文歌 來回切換
類似這樣
enum Song: String {
case chinese
case english
func getChange() {
switch self {
case .chinese:
self = .english
// 切換英文歌
case.english:
self = .chinese
// 切換中文歌
}
}
}
當(dāng)我們編譯的時(shí)候 就會(huì)發(fā)現(xiàn) 報(bào)錯(cuò)了
# Cannot assign to value: 'self' is immutable
這個(gè)時(shí)候我們就用到 mutating了
mutating
在func 前面加上 mutating
,我們就可以在值類型中 改變自身的值了
總結(jié)
參考鏈接
如果有新的知識(shí) 我還會(huì)補(bǔ)充進(jìn)來
以上都是我個(gè)人的一些看法乖杠,可能有不對(duì)的地方
都是第一次學(xué)Swift 分扎,還請(qǐng)多多指教!胧洒!