首先感觉Swift
的枚举确实相对OC
来说功能和语法都扩展很多。首先说一下枚举的定义。
枚举声明的类型是囊括可能状态的有限集,且可以具有附加值。通过内嵌(
nesting
),方法(method
),关联值(associated values
)和模式匹配(pattern matching),枚举可以分层次地定义任何有组织的数据。
Swift中可以制定枚举的类型如下,Swift
可以制定Integer
、Float Point
、String
、三种关联值类型,有的资料说也布尔类型(Boolean
),但本人在测试以后好像不支持,下去也会再查资料验证是否支持Boolean
类型
- 整型(
Integer
) - 浮点数(
Float Point
) - 字符串(
String
)
关联Integer值类型,
enum Direction: Int {
case Left = 0
case Right = 1
case Top = 2
case Bottom = 3
}
关联String值类型,
enum Workday: String {
case Mon = "Monday"
case Tue = "Tuesday"
case Wed = "Wednesday"
case Thu = "Thursday"
case Fri = "Friday"
}
关联Float Point值类型,
enum DouNum: Double {
case π = 3.14159
case e = 2.71828
case φ = 1.61803398874
case λ = 1.30357
}
当关联Integer
或String
类型的时候可以忽略为枚举中的case
赋值,Swift
枚举也能正常工作,如下
enum Direction: Int {
case Left = 1
case Right
case Top
case Bottom
}
enum Direction: String {
case Sun
case Mon
case Tue
case Wed
case Thu
case Fri
case Sat
}
可以通过rawValue
属性来读取枚举的值,如上面的Workday
枚举取出WorkDays
枚举中Sun
的值
case Mon = "Monday"
let EnumValue = WorkDays.Mon.rawValue
print(EnumValue)
//打印的值为- Monday
不过某种情形下,你可能想要通过一个已有的 raw value
来创建一个 enum case
。这种情况下,枚举提供了一个指定构造法:
enum Direction: Int {
case Left = 0
case Right = 1
case Top = 2
case Bottom = 3
}
// 创建一个movement.Right
用例,其raw value
值为1,let rightMovement = Movement(rawValue: 1)
倘若使用rawValue
构造器,切记它是一个可失败构造器( failable initializer )。换言之,构造方法返回值为 可选类型值 ,因为有时候传入的值可能与任意一个 cas都不匹配。比如 Movement(rawValue:42)
。
- 嵌套枚举(
Nesting Enums
)
如果你有特定子类型的需求,可以对enum
进行嵌套。这样就允许你为实际的enum中包含其他明确信息的enum
。以RPG游戏中的每个角色为例,每个角色能够拥有武器,因此所有角色都可以获取同一个武器集合。而游戏中的其他实例则无法获取这些武器(比如食人魔,它们仅使用棍棒)。
enum Character {
enum Weapon {
case Bow
case Sword
case Lance
case Dagger
}
enum Helmet {
case Wooden
case Iron
case Diamond
}
case Thief
case Warrior
case Knight
}
现在,你可以通过层级结构来描述角色允许访问的项目条。
let character = Character.Thief
let weapon = Character.Weapon.Bow
let helmet = Character.Helmet.Iron
说实话上面的例子我个人是无法理解的,首先枚举同级别的项目应该是统一类型的,比如特性(Character
)下面的几个类别根本不是一个类型如头盔(Weapon
)、武器(Helmet
)、小偷(Thief
)、武士(Warrior
)、爵士(Knight
)后三个是人物前两个是是人物的特性
个人感觉下面的例子比较适合用嵌套枚举,如枚举的第一层是人物角色,而人物的角色下面又包含其特有的技能,比如Role
包含Student
,而Student
又有Write
技能
enum Role {
enum Student {
case Sing
case Dance
case Write
}
enum Player {
case Sing
case Dance
case Run
case Jump
}
enum programmer {
case Sing
case Dance
case Read
case Coding
}
case Worker
case Driver
}
现在,我们可以通过层级结构来描述角色允许访问的项目条。
let worker = Role.Worker
let code = Role.programmer.coding
- 包含枚举(
Containing Enums
)
同样地,你也能够在structs
或 classes
中内嵌枚举。接着上面的例子:
struct Character {
enum CharacterType {
case Thief
case Warrior
case Knight
}
enum Weapon {
case Bow
case Sword
case Lance
case Dagger
}
let type: CharacterType
let weapon: Weapon
}
let warrior = Character(type: .Warrior, weapon: .Sword)
同样地,这也将有助于我们将相关的信息集中在一个位置。
- 关联值(
Associated Value
)
关联值是将额外信息附加到 enum case 中的一种极好的方式。打个比方,你正在开发一款交易引擎,可能存在 买 和 卖 两种不同的交易类型。除此之外每手交易还要制定明确的股票名称和交易数量:
- 简单例程(
Simple Example
)
enum Trade {
case Buy
case Sell
}
func trade(tradeType: Trade, stock: String, amount: Int) {}
然而股票的价值和数量显然从属于交易,让他们作为独立的参数显得模棱两可。你可能已经想到要往 struct
中内嵌一个枚举了,不过关联值提供了一种更清爽的解决方案:
enum Trade {
case Buy(stock: String, amount: Int)
case Sell(stock: String, amount: Int)
}
func trade(type: Trade) {}
- 模式匹配(
Pattern Matching
)
如果想要访问这些值,如果你想要访问这些值, 模式匹配 再次救场:
let trade = Trade.Buy(stock: "APPL", amount: 500)
if case let Trade.Buy(stock, amount) = trade {
print("buy \(amount) of \(stock)")
}
- 标签(
Labels
)
关联值不需要附加标签的声明:
enum Trade {
case Buy(String, Int)
case Sell(String, Int)
}
倘若你添加了,那么,每当创建枚举用例时,你都需要将这些标签标示出来。
- 元组参数(
Tuple as Arguments
)
更重要的是,Swift
内部相关信息其实是一个元组,所以你可以像下面这样做:
let tp = (stock: "TSLA", amount: 100)
let trade = Trade.Sell(tp)
if case let Trade.Sell(stock, amount) = trade {
print("buy \(amount) of \(stock)")
}
// Prints: "buy 100 of TSLA"
语法允许您将元组当作一个简单的数据结构,稍后元组将自动转换到高级类型,就比如 enum case 。想象一个应用程序可以让用户来配置电脑:
typealias Config = (RAM: Int, CPU: String, GPU: String)
// Each of these takes a config and returns an updated config
func selectRAM(_ config: Config) -> Config {return (RAM: 32, CPU: config.CPU, GPU: config.GPU)}
func selectCPU(_ config: Config) -> Config {return (RAM: config.RAM, CPU: "3.2GHZ", GPU: config.GPU)}
func selectGPU(_ config: Config) -> Config {return (RAM: config.RAM, CPU: "3.2GHZ", GPU: "NVidia")}
enum Desktop {
case Cube(Config)
case Tower(Config)
case Rack(Config)
}
let aTower = Desktop.Tower(selectGPU(selectCPU(selectRAM((0, "", "") as Config))))
配置的每个步骤均通过递交元组到enum
中进行内容更新。倘若我们从 函数式编程中获得启发,这将变得更好。
infix operator <^> { associativity left }
func <^>(a: Config, f: (Config) -> Config) -> Config {
return f(a)
}
最后,我们可以将不同配置步骤串联起来。这在配置步骤繁多的情况下相当有用。
let config = (0, "", "") <^> selectRAM <^> selectCPU <^> selectGPU
let aCube = Desktop.Cube(config)
- 使用案例(
Use Case Example
)
关联值可以以多种方式使用。常言道:一码胜千言, 下面就上几段简单的示例代码,这几段代码没有特定的顺序。
// 拥有不同值的用例
enum UserAction {
case OpenURL(url: NSURL)
case SwitchProcess(processId: UInt32)
case Restart(time: NSDate?, intoCommandLine: Bool)
}
// 假设你在实现一个功能强大的编辑器,这个编辑器允许多重选择,
// 正如 Sublime Text : https://www.youtube.com/watch?v=i2SVJa2EGIw
enum Selection {
case None
case Single(Range<Int>)
case Multiple([Range<Int>])
}
// 或者映射不同的标识码
enum Barcode {
case UPCA(numberSystem: Int, manufacturer: Int, product: Int, check: Int)
case QRCode(productCode: String)
}
// 又或者假设你在封装一个 C 语言库,正如 Kqeue BSD/Darwin 通知系统:
// https://www.freebsd.org/cgi/man.cgi?query=kqueue&sektion=2
enum KqueueEvent {
case UserEvent(identifier: UInt, fflags: [UInt32], data: Int)
case ReadFD(fd: UInt, data: Int)
case WriteFD(fd: UInt, data: Int)
case VnodeFD(fd: UInt, fflags: [UInt32], data: Int)
case ErrorEvent(code: UInt, message: String)
}
// 最后, 一个 RPG 游戏中的所有可穿戴装备可以使用一个枚举来进行映射,
// 可以为一个装备增加重量和持久两个属性
// 现在可以仅用一行代码来增加一个"钻石"属性,如此一来我们便可以增加几件新的镶嵌钻石的可穿戴装备
enum Wearable {
enum Weight: Int {
case Light = 1
case Mid = 4
case Heavy = 10
}
enum Armor: Int {
case Light = 2
case Strong = 8
case Heavy = 20
}
case Helmet(weight: Weight, armor: Armor)
case Breastplate(weight: Weight, armor: Armor)
case Shield(weight: Weight, armor: Armor)
}
let woodenHelmet = Wearable.Helmet(weight: .Light, armor: .Light)
- 方法和属性(
Methods and properties
)
你也可以在enum
中像这样定义方法:
enum Wearable {
enum Weight: Int {
case Light = 1
}
enum Armor: Int {
case Light = 2
}
case Helmet(weight: Weight, armor: Armor)
func attributes() -> (weight: Int, armor: Int) {
switch self {
case .Helmet(let w, let a): return (weight: w.rawValue * 2, armor: w.rawValue * 4)
}
}
}
let woodenHelmetProps = Wearable.Helmet(weight: .Light, armor: .Light).attributes()
print (woodenHelmetProps)
// prints "(2, 4)"
枚举中的方法为每一个 enum case
而“生”。所以倘若想要在特定情况执行特定代码的话,你需要分支处理或采用switch
语句来明确正确的代码路径。
enum Device {
case iPad, iPhone, AppleTV, AppleWatch
func introduced() -> String {
switch self {
case AppleTV: return "\(self) was introduced 2006"
case iPhone: return "\(self) was introduced 2007"
case iPad: return "\(self) was introduced 2010"
case AppleWatch: return "\(self) was introduced 2014"
}
}
}
print (Device.iPhone.introduced())
// prints: "iPhone was introduced 2007"
- 属性(
Properties
)
尽管增加一个存储属性到枚举中不被允许,但你依然能够创建计算属性。当然,计算属性的内容都是建立在枚举值下或者枚举关联值得到的。
enum Device {
case iPad, iPhone
var year: Int {
switch self {
case iPhone: return 2007
case iPad: return 2010
}
}
}
- 静态方法(
Static Methods
)
你也能够为枚举创建一些静态方法(static methods
)。换言之通过一个非枚举类型来创建一个枚举。举个栗子,我们在实际开发的时候,有时tableView
有时需要返回两种Cell
,我们可以根据后台返回的数据来判断返回那种cell
表示。
enum StatusCellID: String
{
case NormalCell = "NormalCell"
case ForwardCell = "ForwardCell"
/// 静态函数, 相当于类方法
/// 根据微博数据,返回对应的标示符
static func cellId(status: Status) ->String
{
return status.retweeted_status != nil ? StatusCellID.ForwardCell.rawValue : StatusCellID.NormalCell.rawValue
}
}
- 可变方法(
Mutating Methods
)
方法可以声明为mutating
。这样就允许改变隐藏参数self
的case
值了。
enum TriStateSwitch {
case Off, Low, High
mutating func next() {
switch self {
case Off: self = Low
case Low: self = High
case High: self = Off
}
}
}
var ovenLight = TriStateSwitch.Low
ovenLight.next()
// ovenLight 现在等于.On
ovenLight.next()
// ovenLight 现在等于.Off
- 小结(
To Recap
)
至此,我们已经大致了解了Swift
中枚举语法的基本用例。在开始迈向进阶之路之前,让我们重新审视文章开篇给出的定义,看看现在是否变得更清晰了。
现在我们已经对这个定义更加清晰了。确实,如果我们添加关联值和嵌套, enum
就看起来就像一个封闭的、简化的 struct
。相比较 struct
,前者优势体现在能够为分类与层次结构编码。
// Struct Example
struct Point { let x: Int, let y: Int }
struct Rect { let x: Int, let y: Int, let width: Int, let height: Int }
// Enum Example
enum GeometricEntity {
case Point(x: Int, y: Int)
case Rect(x: Int, y: Int, width: Int, height: Int)
}
方法和静态方法的添加允许我们为 enum
附加功能,这意味着无须依靠额外函数就能实现。
// C-Like example
enum Trade {
case Buy
case Sell
}
func order(trade: Trade)
// Swift Enum example
enum Trade {
case Buy
case Sell
func order()
}
*参考原文 * http://swift.gg/2015/11/20/advanced-practical-enum-examples/