从 枚举(enum) 到 Swift

2019-11-09  本文已影响0人  overla5

枚举入门

来,小明 给我说说什么是枚举

小明: 你把手举起来

然后呢?

小明: 你看我的手 举没举 ?

 

基本定义

枚举作为 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 初始化,是一种隐藏了 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

 

小结

case let .Other(s1, s2, s3):

 

扩展 和协议 的第二种写法

我们也可以 使用协议(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 ,我们就可以在值类型中 改变自身的值了

总结

参考链接

SwiftGG

如果有新的知识 我还会补充进来

以上都是我个人的一些看法,可能有不对的地方

都是第一次学Swift ,还请多多指教!!
上一篇下一篇

猜你喜欢

热点阅读