从 枚举(enum) 到 Swift

枚举入门

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

小明: 你把手举起来

然后呢?

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

 

基本定义

枚举作为 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 ,我们就可以在值类型中 改变自身的值了

总结

参考链接

SwiftGG

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

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

都是第一次学Swift ,还请多多指教!!
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容