swift中的枚举( Enumerations
)真的是太灵活了,感觉自己已经驾驭不了这匹野马了😭。
swift中的枚举特点:
- 一等公民(first-class citizen)
- 枚举是值类型,如果需要改变其成员值,需要通过
mutating
关键词 - 支持类,结构体中所支持的计算属性(
computed properties
),就是其它语言中的getter
,setter
方法 - 支持实例方法,提供枚举值相关联的功能
- 还可以定义构造函数(mmp,太难了)
- 还可以在原始实现的基础上扩展其功能
- 可以遵循协议(protocols) 提供标准的功能
- 和C以及OC不同的是,swift中的枚举成员在创建的时候不会被赋予一个默认的整型值
- 其中涉及到的关键词和属性有:
indirect
,CaseIterable
,allCase
,rawValue
- 涉及到的知识点: 关联值, 原始值,递归枚举
1.语法
使用 enum
关键词定义枚举类型,这个和其它语言一致 例如
typescript中定义枚举:
enum Direction {
Up, // 默认的赋值为0,底下的依次 +1
Down,
Left,
Right
}
swift中: 使用 case
关键词来定义一个新的枚举成员
enum Direction {
// Up,Down...也称之为 成员值
case Up // 此处不会默认的赋值 0
case Down
case Left
case Right
}
// 等价于
enum Direction {
case Up, Down, Left, Right
}
使用:
var direction = Direction.Up
// 或者
var direction: Direction = .Up
2.用Switch语句匹配枚举值
可以使用switch匹配单个枚举值
enum Direction {
case Up, Down, Left, Right
}
var direction: Direction = .Up
switch direction {
case .Up:
print("向上")
case .Down:
print("向下")
case .Left:
print("向左")
case .Right:
print("向右")
// default语句可写 可不写
default:
print("其它方向")
}
3.关联值(Associated Values)
作用:
- 将其它类型的关联值和成员值一起进行存储,这样可以连同成员值一起存储额外的自定义信息
- 每次在代码中使用该枚举成员时,还可以修改这个关联值
示例:一种商品有2种编码方式进行追踪:
- 按照
1-5-5-1
格式的数字('1' 表示1位数字 '5' 表示5位数字)表示商品的UPC一维条形码 - 使用字符串的形式QR码
enum Barcode {
// 理解方式
// 成员值是具有 (Int, Int, Int, Int) 类型相关联值的upc
case upc(Int, Int, Int, Int) // 一维条形码
// 具有 String 类型关联值的 qrCode
case qrCode(String) // QR码
}
// 使用
// 定义一个商品的一维条形码
var productBarcode = Barcode.upc(8, 89201, 28993, 1)
// 也可以定义为一个QR码
prodictBarcode = .qrCode("ABCDEFGHEISLDK")
// 配合switch
switch productionBarcode {
// 其中 let 表示提取变量 这个可以参考控制流switch章节
case let .upc(numberSystem, manufacturer, product, check):
print("UPC: \(numberSystem), \(manufacturer), \(product), \(check)")
case let .qrCode(productCode):
print("QR code: \(productCode)")
}
4.迭代枚举类型(Iterating over Enumeration)
有时候我们可能需要将枚举中所有的case情况组成一个集合,可以让枚举类型扩展CaseIterable
协议。
另外swift通过 allCases
属性暴露了枚举类型中所有case 的集合:
注意 Xcode 10.0+
版本才支持这个属性
// 扩展 CaseIterable 协议
enum Beverage: CaseIterable {
case coffee, tea, juice
}
// allCases 属性暴露了枚举中cases集合
let numberOfChoices = Beverage.allCases.count // 3
// 使用for-in进行迭代
for beverage in Beverage.allCases {
print(beverage)
}
// coffee
// tea
// juice
5.原始值(Raw Values)
作为关联值的替代选择,枚举成员可以被默认值(也称为原始值)预填充,这些原始值的类型必须相同
原始值的理解: 即定义枚举的时候,把枚举的类型也定义了,比如 enum Color: String
, 定义其类型为 String
类型
和关联值的区别:
- 关联值是创建一个局域枚举成员的常量或变量时才设置值,关联值可以变化
- 原始值针对特定的枚举成员类型,原始值会预先填充值,并且始终不变
示例:使用ASCII码作为原始值的枚举
// 这里使用Character类型作为原始值类型
// 还可以是 字符串,整型或者浮点类型
enum ASCIIControlChar: Character {
case tab = "\t"
case lineFeed = "\n"
case carriageReturn = "\r"
}
原始值的隐式赋值
当原始值类型为整型或者字符串类型时,不需要显式的给每个枚举成员设置原始值,swift会自动的为你赋值
enum Direction: Int {
// 依次 +1 进行类推
case Up = 1, Down, Left, Right
}
使用 rawValue
属性可以访问枚举成员的原始值:
var left: Direction = .Left
// 原始值
left.rawValue // 3
对字符串类型:隐式的为该枚举成员的名称
enum CompassPoint: String {
case ease, south, west, north
}
// 使用 rawValue
let north = CompassPoint.north.rawValue // "north"
使用原始值初始化枚举类型
如果枚举类型使用了原始值,自动获取一个初始化方法(呵呵,什么鬼),这个方法接收一个 rawValue
的参数,参数类型即为原始值类型,返回值则是枚举成员或 nil
另外,原始值构造器是一个可失败构造器(failable initializer)
例如,调用构造器创建一个新的枚举实例
enum Direction: Int {
// 依次 +1 进行类推
case Up = 1, Down, Left, Right
}
// 使用构造器,参数为rawValue 创建一个枚举实例
let possibleDirection = Direction(rawValue: 3)
// possibleDirection 的类型为 Direction? (可选类型)
// 值为 Direction.Left
// 如果不存在该值,这表示构造失败
let antherDirection = Direction(rawValue: 10)
// possibleDirection 的类型为 Direction
// 值为 nil
6.递归枚举
特点:
- 递归枚举是一种枚举类型
- 它有一个或多个枚举成员使用 枚举类型的实例作为关联值
- 编译器会插入一个间接层
- 可以在枚举成员前加上
indirect
来表示该成员可递归 - 也可以在枚举类型的开头加上
indirect
关键词表示所有枚举成员都可递归
// 部分枚举成员可递归枚举
// 上面示例中 3个成员 都是 关联值
enum ArithExpression {
case number(Int)
// 使用一个或多个枚举成员的实例作为关联值
// 使用 indirect 关键词
indirect case addition(ArithExpression, ArithExpression)
indirect case multi(ArithExpression, ArithExpression)
}
// 在 enum 前加上 'indirect' 表示所有枚举成员都可递归枚举
indirect enum ArithExpression {
case number(Int)
case addition(ArithExpression, ArithExpression)
case multi(ArithExpression, ArithExpression)
}
// 使用
let five = ArithExpression.number(5)
let four = ArithExpression.number(4)
let sum = ArithExpression.addition(five, four)
let multiResult = ArithExpression.multi(sum, ArithExpression.number(2))
要操作具有递归性质的数据结构,使用递归函数事一种直接的方式:
func evaluate(_ expression: ArithExpression) -> Int {
switch expression {
case let .number(value):
return value
case let .addition(left, right):
return evaluate(left) + evaluate(right)
case let .multi(left, right):
return evaluate(left) * evaluate(right)
}
}
感觉这个这么复杂,估计用到的地方也比较少,了解即可
8.使用mutating 改变枚举成员的值
因为枚举类型是值类型,赋值一般都是通过拷贝的方式,所以原来的值不会改变,swift中提供了 mutating
关键词用来改变值类型的值
// 风扇开关
// 有3个档位 Off, Low, High
// next 方法用来 表示其下一档位
enum FanSwitch {
case Off, Low, High
mutating func next() {
switch self {
case .Off:
self = .Low
case .Low:
self = .High
case .High:
self = .Off
}
}
}
// 定义为 Low 档位
var fan = FanSwitch.Low
fan.next() // High
fan.next() // Off
fan.next() // Low
8.枚举是值类型
enum CompassPoint {
case North, South, East, West
}
var currentDirection = CompassPoint.West
// 将上面的枚举实例赋值给另一个常量 通过拷贝的方式
let rememberedDirection = currentDirection
// 改变原来的枚举变量的值
currentDirection = .East
// 并不会影响到其赋值的值
if rememberedDirection == .West {
print("The remembered direction is still .West")
}
// 打印 "The remembered direction is still .West"
9.关于枚举其它
可选类型是一个泛型枚举
其结构大致如下
enum Optional<T> {
case None
case Some(T) // 关联值
}
// 示例1
var str: String? = nil
// 等价于
var str = Optional<String>.None
// 示例2
var str2: String? = "Holo"
// 等价于
var str2 = Optional<String>.Some("Holo")
// 对于可选类型 需要解包
var showStr = str2!
// 这个过程等价于
switch str2 {
case Some(let value):
showStr = value
case None:
// 解包发生异常
}
枚举内部访问case
enum Turn {
case left
case right
var reminder: String {
switch self {
case .left:
return "左转"
case .right
return "右转"
}
}
}
类型属性(静态属性或者类属性)
enum SomeEnum {
static var storedTypeProty = 10
static var computedTypeProperty: Int {
// get, set 方法
}
}
网友评论