枚举入门
来,小明 给我说说什么是枚举
小明: 你把手举起来
然后呢?
小明: 你看我的手 举没举 ?
基本定义
枚举作为 Swift 的 一等类型
, 我们正好可以通过枚举, 来 一一列举 Swift 其它 强有力的 类型
首先写出枚举的 2种表达方式
它们被放在一个大括号里
,纵向排列,互不干扰
enum SwiftType {
case protocol
case enum
case struct
case class
case tuple
case function
}
当然它们也可以抱团相拥在一起, 以 逗号
来互相敬畏
enum ProgrammingLanguage {
case protocol, enum, struct, class, tuple, function
}
掌握以上几种类型,便可呼风唤雨
那么我就以初学者的姿态记录一下枚举吧
原始值 rawValue
Swift 的枚举 并不会像 OC 那样固定的赋予一个默认的整形值
,并没有所谓的从上到下,原始值
也不是 从0到5
它们的原始值(rawValue)
也可以是其它类型
enum RawValueType {
case 整形
case 字符
case 字符串
case 浮点型
}
但是它们必须拥有一个共同的类型
且 原始值唯一
像 ProgrammingLanguage 这种没有指明
原始值类型的枚举 没有原始值,
它们的实例 也没法用点语法 点出 rawValue
赋予原始值类型
如果想像OC 一样 拿到原始值,我们可以指定类型
enum ProgrammingLanguage: Int {
case Swift
case OC
case Python
case Java
}
一旦我们给定整形,也就意味着它们默认是从0
开始的
下一个枚举的原始值 = 上一个枚举的原始值 + 1
⚠️只有整形是这样 依次累加哦
var p = ProgrammingLanguage.OC.rawValue
print(p)
// 打印 1,因为 Swift 默认是 0
现在 我们做一些改动
enum ProgrammingLanguage: Int {
case Swift = 2
case OC
case Python = 6
case Java
}
var p = ProgrammingLanguage.OC.rawValue
print(p)
// 打印 3,因为 Swift 是 2
print(p1)
// 打印 7,因为 Python 是 6
字符串隐式原始值
如果我们指定枚举类型 是 String
,那么其中的 case 对应的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 是可选型,因为 枚举并不知道 你传进去的 rawValue 是否存在
# 下面会说到,这种方式的实例化 是一个可失败的构造器
Switch 匹配枚举值
一般来说,我们写出枚举 是为了区分不同的case,和 OC 一样 Switch
就成了我们 匹配枚举值的 首选
Swift 的 枚举情况 分为2种
// 登录方式的枚举
enum LoginWay {
case Apple
case QQ
case Wechat
case Weibo
}
let way = LoginWay.Apple
第一种: 穷尽所有
* 遍历所有大括号内的case,一个不漏,不用写defult
* 如果遗漏了,并且没有defult 将会报错
switch way {
case .Apple:
print("apple")
case .QQ:
print("qq")
case .Wechat:
print("wechat")
case .Weibo:
print("weibo")
}
// 打印 "apple"
第二种: 投其所好
* 只展示部分我关心的case,其余的 用 defult 代表
* 不用展示全部
switch way {
case .QQ:
print("qq")
case .Weibo:
print("weibo")
default:
print("其它")
}
// 打印 "其它"
* 关联值
什么是关联值 ?
我们来看一个栗子
// 定义一个不同交通工具 上班时间 ,我们可以自由选择上班方式
enum OnTheWayTime {
case bicycle(Int) // Int 类型关联值 的 bicycle
case taxi(Int) // Int 类型关联值 的 taxi
case bus(time: Int) // Int 类型关联值 的 bus
case horse(String) // String 类型关联值 的 horse
}
var t = OnTheWayTime.bicycle(60)
// "实例化一个变量",并且给成员变量 bicycle 关联 Int值 60
如果我们想要 改变 t ,也就是我们的出行方式
在t 的类型已经确定的情况下,我们可以不用带枚举名称,直接 .
t = .taxi(30)
我们去 Switch 遍历 这个枚举,看一下关联值使用方式
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)")
}
可以提取关联值 用 "let / var" 修饰
通过值绑定,生成的 "局部变量" 就与 "关联值" 相连接
修改t ,再去遍历,t 是可以任意变化的
t = .horse("很久")
打印:骑马要很久
optional 关联值
optional(可选型) 是比较常用的枚举,它的成员值 .some
也是通过关联值的方式
var age: Int?
age = 17
switch age {
case .none:
print("age 为 nil")
case .some(let value):
print("age 的值是: \(value)")
}
// 打印: age 的值是 17
问题
关联值 的成员 有rawValue 吗?
答案是 没有的
因为 rawValue 是遵从了 RawRepresentable
协议,协议中通过 associatedtype
来关联 rawValue, associatedtype 是用来定义 在协议中使用的 关联类型
,虽然这个关联类型是不确定的,但是它们是统一的。
而 有关联值的
枚举,它们的类型是不统一
的,所以无法使用 rawValue
结论
- 我们可以把关联值当做一个变量,
关联之后的成员值 是可变的
- 关联值 和 原始值不同,
原始值的值 从开始 就是确定的,无法改变
- 关联值 无法使用 rawValue 属性,因为它们类型无法 统一
枚举的构造过程
构造过程
:保证新实例在第一次使用前完成正确的初始化
除了在上述中 提到 的 rawValue 初始化
,是一种隐藏了 init?
的可失败的 构造器之外,
我们还可以自定义 不隐藏的 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后面 加 问号吗?可以不加defult 吗?
答案是 不可以的
虽然 我们列举的case 是 一一俱全的,但是我们并不能保证 初始化构造的时候 你会传入什么东西
,所以这个构造是可能
会失败的,结果是可选的,所以就得加 ? ,就需要defult来 处理不存在的 case
枚举的属性
计算属性
来,看栗子
吃开封菜的时候到了
通过对 kfc 的点单方式 单点/套餐 我们写了一个枚举,外界通过调用实例的 description 来获得 描述
enum KFCFood {
case familyFood(Int)
case Other(String, String, String)
var description: String {
switch self {
case .familyFood(let num):
return "今天我一个人吃了 \(num) 个全家桶"
case let .Other(s1, s2, s3):
return "今天晚餐吃了\(s1) \(s2) 还有 \(s3)"
}
}
}
var k = KFCFood.familyFood(2)
print(k.description) // 今天我一个人吃了 2 个全家桶
k = .Other("汉堡", "可乐", "薯条")
print(k.description) // 今天晚餐吃了汉堡 可乐 还有 薯条
ps: 可乐 汉堡 和薯条 不也是套餐吗 你个low 狗
我们定义了一个 KFC 的枚举
通过 关联值 + 计算属性
来 存储 以及 获得 description
小结
- 因为这里有关联值,所以没法通过 rawValue的方式 初始化,也就是说如果通过关联值初始化,就意味着 得到的实例 都是存在的,
switch 里 不需要 defult
case let .Other(s1, s2, s3):
- 如果说关联值得个数 不止一个,那么我们使用的时候,可以把修饰 局部变量的
let/var 提到 最前面
扩展 和协议 的第二种写法
我们也可以 使用协议(Protocols)
和协议扩展(Protocol Extension)
高大上 有没有
通过协议 以及 协议扩展可以更好的 将 成员值 与 属性/方法 实现分离开
,
代码也就自然而然的 通俗易懂了
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 "今天我一个人吃了 \(num) 个全家桶"
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
那么如果我们想在方法内改变 自身的值呢?
比如 我们想中文歌 和 英文歌 来回切换
类似这样
enum Song: String {
case chinese
case english
func getChange() {
switch self {
case .chinese:
self = .english
// 切换英文歌
case.english:
self = .chinese
// 切换中文歌
}
}
}
当我们编译的时候 就会发现 报错了
# Cannot assign to value: 'self' is immutable
这个时候我们就用到 mutating了
mutating
在func 前面加上 mutating
,我们就可以在值类型中 改变自身的值了
总结
参考链接
如果有新的知识 我还会补充进来
以上都是我个人的一些看法,可能有不对的地方
都是第一次学Swift ,还请多多指教!!