首页 > 代码库 > Swift 学习- 09 -- 枚举
Swift 学习- 09 -- 枚举
// 递归枚举
// 美家居为一组相关的值定义了一个共同的类型, 使你可以在代码中以类型安全的的方式使用这些值.
// 如果你熟悉C语言, 你会知道在C语言中, 枚举会为一组整型值分配相关联的名称, swift 中的枚举更加的灵活, 不必给每一个枚举成员提供一个值,如果给枚举成员提供一个值 (称为原始值), 则该值的类型可以是字符串,字符, 或者是一个整型值或浮点数,
// 此外, 枚举成员可以指定任意类型的关联值存储到枚举成员中, 你可以在一个枚举中定义一组相关的枚举成员, 每一个枚举成员都可以有适当类型的关联值,
// 枚举语法
// 使用enum 关键字来创建枚举并且把它们的真个定义放在一堆大括号内
enum SomeEnumeration{
// 枚举定义放在这里
}
// 下面用枚举表示指南针四个方向的例子
enum CompassPoint {
case north
case south
case east
case west
}
// 枚举中定义的值 (如 north, south, east ,west) 是这个枚举的成员值(或成员), 你可以使用 case 关键字来定义一个新的枚举成员值
// 注意 : 与 C语言和 OC 不同, swift 的枚举成员在被创建时不会被赋予一个默认的整型值,在上面的 CompassPoint 例子中, north, south ,east 和 west 不会被隐式地赋值为0,1,2,3 ,相反,这些枚举成员本身就是完备的值,这些值的类型是已经是明确定义好的 CompassPoint 类型
// 多个成员可以出现在同一行上, 用逗号隔开
enum Planet {
case mercuty, venus,earth, jupiter, saturn, uranus, neptune
}
// 每个枚举定义了一个全新的类型, 像 Swift 中其他类型一样, 他们的名字,应该以一个大字母开头, 给枚举类型起一个单数名字而不是复数名字, 以便于读起来更加容易理解
var directionToHead = CompassPoint.west
// directionToHead 的类型可以在它被 CompassPoint 的某个值初始化时推断出来, 一旦 directionToHead 被声明为 CompassPoint 类型, 你可以使用更简短的点语法将其设置为另一个 CompassPoint 的值,
directionToHead = .east
// 当 directionToHead 的类型已知时, 再次为其赋值可以省略枚举类型名, 在使用具有显式类型的枚举值时, 这种写法让代码有更好的可读性
// 使用 Swift 语句匹配枚举值
// 你可以使用 Swift 语句匹配单个值:
directionToHead = .south
switch directionToHead {
case .north:
print("Lots of planets have a north")
case .south:
print("Watch out for penguins")
case .east:
print("Where the sun rises")
case .west:
print("Where the skies are blue")
default:
print("none")
}
// 在判断一个枚举类型的值时 ,Swift 语句必须穷举素有的情况, 当不需要匹配每个枚举成员的时候, 你可以提供一个 default 分支来涵盖所有未明确处理的枚举成员
let somePlanet = Planet.earth
switch somePlanet {
case .earth:
print("Mostly harmless")
default:
print("Not a safe place for humans")
}
// 关联值
// 有时候能够把其他类型的关联值和成员值一起存储起来会更有用, 这能让你连同成员值一起存储额外的自定义信息,并且你每次在代码中使用该枚举成员时, 还可以修改这个关联值
// 你可以定义 Swift 枚举来存储任意类型的关联值, 如果需要的话, 每个枚举成员的关联值类型可以各部相同, 枚举的这个特性跟其他语言中的可识别联合, 标签联合, 或者变体相似
// 例如: 假设一个库存跟踪系统需要利用不同类型的条形码来跟踪商品,有四个数字,其他商品上标有 QR 码格式的二维码, 它是可以使用任何字符的字符串
// 在 Swift 中, 使用如下方式定义表示两种商品条形码的枚举
enum Barcode{
case upc(Int,Int,Int,Int)
case qrCode(String)
}
// 以上可以这么理解 : "定义一个名为 BarCode 的枚举类型, 它的一个成员是具有 (Int, Int, Int, Int) 类型关联值的upc, 另一个成员是具有 String 类型关联值的 qrCode"
// 这个定义不提供任何 Int 或者 String 类型的关联值, 它只是定义了, 当 BarCode 常量和变量等于 Barcode.upc 或者 Barcode.qrcode 时, 可以存储的关联值的类型
// 然后可以使用任意一种条形码类型创建新的条形码, 例如:
var productBarcode = Barcode.upc(8, 85909, 51226, 3)
// 上面的例子创建了一个名为 productBarcode 的变量, 并将 Barcode.upc 赋值给它,
// 同一个商品可以被分配一个不同类型的条形码, 例如:
productBarcode = .qrCode("BJUIBHYUGVUTYFVFY")
// 这时,原始的 Barcode.upc 和其整数关联值被新的 Barcode.qrCode 和其字符串关联值所替代, Barcode 类型的常量和变量可以存储一个 .upc 或者一个 .qrCode,(连同他们的关联值), 但是在同一时间只能存储两个值中的一个
// 像先前那样,可以使用一个 switch 语句来检查不同的条形码类型。然而,这一次,关联值可以被提取出来作为 switch 语句的一部分。你可以在switch的 case 分支代码中提取每个关联值作为一个常量(用let前缀)或者作为一个变量(用var前缀)来使用:
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).")
}
// 打印 "QR code: ABCDEFGHIJKLMNOP."
// 如果一个枚举成员的所有关联值都被提取为常量,或者都被提取为变量,为了简洁,你可以只在成员名称前标注一个let或者var:
switch productBarcode {
case let .upc(numberSystem, manufacturer, product, check):
print("UPC: \(numberSystem), \(manufacturer), \(product), \(check).")
case let .qrCode(productCode):
print("QR code: \(productCode).")
}
// 输出 "QR code: ABCDEFGHIJKLMNOP."
// 原始值
// 在上个例子中, 演示了如何声明存储不同类型关联值的枚举成员, 作为关联值的替代选择, 枚举成员可以被默认值 (称为原始值) 预填充, 这些原始值的类型必须相同
// 原始值 可以是字符串, 字符,或者任意整型或是浮点型, 每个原始值在枚举声明中必须唯一的.
// 注意 : 原始值和关联值是不同的,原始值是在定义枚举是被预先填充的值, 对于一个特定的枚举成员,它的原始值始终不变, 关联值是创建一个基于枚举成员的变量或是常量是才设置的值, 枚举成员的关联值可以变化
// 原始值的隐式赋值
// 在使用原始值为整数或者字符串类型的枚举时, 不需要显式地为每一个枚举成员设置原始值, Swift 将会自动为你赋值
// 例如,当使用整数作为原始值时,隐式赋值的值依次递增 1, 如果第一个枚举成员没有设置原始值, 其原始值将为0
enum Planet2: Int {
case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
}
// 在上面的例子中,Plant.mercury的显式原始值为1,Planet.venus的隐式原始值为2,依次类推。
// 当使用字符串作为枚举类型的原始值时,每个枚举成员的隐式原始值为该枚举成员的名称。
// 下面的例子是CompassPoint枚举的细化,使用字符串类型的原始值来表示各个方向的名称:
enum CompassPoint2: String {
case north, south, east, west
}
// 上面的例子中, CompassPoint2.south 拥有隐式原始值 south ,依次类推
// 使用枚举成员的 rawValue 属性可以访问该枚举成员的原始值
let earthsOrder = Planet2.earth.rawValue
let sunsetDirection = CompassPoint2.west.rawValue
// 使用原始值初始化枚举实例
// 如果在定义枚举类型的时候使用了原始值, 那么将会自动获得一个初始化方法, 这个方法接收一个叫做 rawValue 的参数,参数类型即为原始值类型, 参数类型即为原始值的类型, 返回值则是枚举成员或是 nil, 你可以使用这个初始化方法来创建一个新的枚举实例
// 这个例子利用原始值 7 创建了新的枚举成员 uranus:
let possiblePlanet = Planet2(rawValue: 7)
// 然而, 并非所有 Int 值都可以找到一个匹配的枚举成员, 因此, 原始值构造器总是返回一个可选的枚举成员, 在上面的例子中, possiblePlanet 是 Planet2? 类型, 或者说是 ‘可选的 Planet2‘
// 注意 : 原始值构造器是一个可失败的构造器, 因为并不是每一个原始值都有与之对应的枚举成员
// 如果你试图寻找一个位置 为 11 的枚举成员, 通过原始值构造器返回可选的 Planet2 值将是 nil
// 递归枚举
// 递归枚举是一种枚举类型, 它有一个或多个枚举成员使用该枚举类型的实例作为关联值,使用递归枚举时,编辑器会插入一个间接层, 你可以在枚举成员前加上 indirect 来表示该成员可递归
// 例如,下面的例子中, 枚举类型存储了简单的算数表达式
enum ArithmeticExpression {
case number(Int)
indirect case addition(ArithmeticExpression, ArithmeticExpression)
indirect case multiplication(ArithmeticExpression,ArithmeticExpression)
}
// 你也可以在枚举类型开头加上 indirect 关键字来表明它的所有成员都是可递归的
indirect enum ArithmeticExpression2 {
case number(Int)
case addition(ArithmeticExpression, ArithmeticExpression)
case multiplication(ArithmeticExpression, ArithmeticExpression)
}
// 上面定义的枚举类型可以存储三种算术表达式:纯数字、两个表达式相加、两个表达式相乘。枚举成员addition和multiplication的关联值也是算术表达式——这些关联值使得嵌套表达式成为可能。例如,表达式(5 + 4) * 2,乘号右边是一个数字,左边则是另一个表达式。因为数据是嵌套的,因而用来存储数据的枚举类型也需要支持这种嵌套——这意味着枚举类型需要支持递归。下面的代码展示了使用ArithmeticExpression这个递归枚举创建表达式(5 + 4) * 2
let five = ArithmeticExpression.number(5)
let four = ArithmeticExpression.number(4)
let sum = ArithmeticExpression.addition(five, four)
let product = ArithmeticExpression.multiplication(sum, ArithmeticExpression.number(2))
Swift 学习- 09 -- 枚举