Swift读书笔记之枚举

282 阅读3分钟

IMG_0514.jpg

“东海帝王,奇迹的复活” --- 《赛马娘第二季》

前言

枚举为一组相关值定义了一个通用类型,从而可以让你在代码中类型安全地操作这些值。

Swift中的枚举更加灵活,并且不需给枚举中的每一个成员都提供值。如果一个值(所谓“原始”值)要被提供给每一个枚举成员,那么这个值可以是字符串、字符、任意的整数值,或者是浮点类型。

而且,枚举成员可以指定任意类型的值来与不同的成员值关联储存,即枚举成员中的值可能是多种类型。枚举具有自己权限的一种类型,一句话:功能很强大,用法很nb。

枚举语法

/// 多行
enum RacingHorse {
    case specialWeek
    case silenceSuzuka
    case goldShip
    case mejiroMcQueen
}
/// 单行 用逗号隔开
enum RacingHorse {
    case riceShower, grassWonder, biwaHayaHide
}
/// 使用
let firstHorse = RacingHorse.specialWeek
/// 类型推断
let secondHorse: RacingHorse = .riceShower

关联值

这里就和我们平时使用的OC和C的枚举不一样了

/// 定义一个叫做 Barcode的枚举类型,它要么用 (Int, Int, Int, Int)类型的关联值获取 upc 值,要么用 String 类型的关联值获取一个 qrCode的值。
enum Barcode {
    case upc(Int, Int, Int, Int)
    case qrCode(String)
}
/// 关联了一个元组的Barcode.upc
var productBarcode = Barcode.upc(8, 85909, 51226, 3)
productBarcode = .qrCode("ABCDEFGHIJKLMNOP")
/// 这时productBarcode的值会被Barcode.qrCode取代 这时的关联值为"ABCDEFGHIJKLMNOP"的字符串

相关值可以作为常量或变量在switch的case中使用:

switch productBarcode {
case .upc(let numberSystem, let manufacturer, let product, let check):
    print("UPC: \(numberSystem), \(manufacturer), \(product), \(check).")
case .qrCode(let productCode):
    print("QR code: \(productCode).")
}
/// 如果都被提取成常量或者变量,可以提前标注
switch productBarcode {
case let .upc(numberSystem, manufacturer, product, check):
    print("UPC: \(numberSystem), \(manufacturer), \(product), \(check).")
case var .qrCode(productCode):
    print("QR code: \(productCode).")
}

原始值

作为相关值的另一种选择,枚举成员可以用相同类型的默认值预先填充(称为原始值)。

enum ASCIIControlCharacter: Character {
    case tab = "\t"
    case lineFeed = "\n"
    case carriageReturn = "\r"
}

隐式指定的原始值

enum Planet: Int {
    case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
}
/// venus原始值为2 以此类推
enum racingHorse: String {
    case specialWeek
    case silenceSuzuka
    case goldShip
    case mejiroMcQueen
}
racingHorse.specialWeek的原始值为"specialWeek"

可以通过rawValue属性来访问一个枚举成员的原始值

let earthsOrder = Planet.earth.rawValue /// earthsOrder is 3
let horseName = racingHorse.specialWeek.rawValue /// horseName is "specialWeek"

从原始值初始化

let possiblePlanet = Planet(rawValue: 7)
// possiblePlanet is of type Planet? and equals Planet.Uranus
// 返回的是可选类型,原始值如果不存在将会是nil

递归枚举

枚举在对序号考虑固定数量可能性的数据建模时表现良好,比如用来做简单整数运算的运算符。这些运算符允许你组合简单的整数数学运算表达式比如5到更复杂的比如5+4.

数学表达式的一大特征就是它们可以内嵌。比如说表达式(5 + 4) * 2 在乘法右手侧有一个数但其他表达式在乘法的左手侧。因为数据被内嵌了,用来储存数据的枚举同样需要支持内嵌——这意味着枚举需要被递归。

递归枚举是拥有另一个枚举作为枚举成员关联值的枚举。当编译器操作递归枚举时必须插入间接寻址层。你可以在声明枚举成员之前使用 indirect关键字来明确它是递归的。

自己写个例子,打游戏时有伤害计算什么的,要计算暴击,护甲什么的。例子烂,凑合看

indirect enum damage {
    case number(Double)
    case crit(damage, damage)
    case reduce(damage, damage)
}

let attack = damage.number(100.0) /// 攻击力
let multipleCrit = damage.number(2.0) /// 暴击倍数
let multipleReduce = damage.number(0.8) /// 减伤
let attackOnce = damage.crit(attack, multipleCrit) /// 打出去伤害
let underAttack = damage.reduce(attackOnce, multipleReduce) /// 收到伤害

func evaluate(_ expression: damage) -> Double {
    switch expression {
    case let .number(attackValue):
        return attackValue
    case let .crit(attackValue, multiple):
        return evaluate(attackValue) * evaluate(multiple)
    case let .reduce(attackValue, multiple):
        return evaluate(attackValue) * evaluate(multiple)
    }
}

print(evaluate(underAttack))
/// print 160.0