来源: https://blog.csdn.net/itchosen/article/details/77749152
这篇总结来自于上述链接,吸收融化转载过来。原文比较长。
声明枚举:
enum JButtonStyle {
case normal
case top
}
使用:
let aa: JButtonStyle = .normal
let bb = JButtonStyle.top
switch aa {
case .normal: break
case .top: break
default: break
}
if aa == .normal { }
枚举值
Swift枚举中支持以下四种关联值类型:
- 整型(Integer)
- 浮点数(Float Point)
- 字符串(String)
- 布尔类型(Boolean)】
其中整型
和字符串
如果不绑定值,系统会默认绑定。
enum PlayStyle: String {
case stop = "开始"
case start //默认 start = "start"
}
可以使用rawValue
方法来读取枚举绑定的值
print("\(PlayStyle.stop.rawValue)")
//开始
可以关联值的枚举还可以通过 value 来构造一个枚举类型
let valueInit = PlayStyle(rawValue: "")
其他的不能通过 rawValue 方法来创建,否则会报cannot be constructed because it has no accessible initializers不能识别初始化方法错误。
但是因为这个值不在枚举范围之内,所以rawValue
可能构造一个nil
的枚举。
嵌套枚举(Nesting Enums)
enum MovieStye {
case love
case comedy
enum PlayStyle: String {
case stop = "开始"
case start //默认 start = "start"
}
}
包含枚举(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 Trade {
case Buy(stock: String, amount: Int)
case Sell(stock: String, amount: Int)
}
func trade(type: Trade) {}
也可以省去标签,这样写
enum Trade {
case Buy(String, Int)
case Sell(String, Int)
}
访问
let trade = Trade.Buy(stock: "APPL", amount: 500)
if case let Trade.Buy(stock, amount) = trade {
print("buy \(amount) of \(stock)")
}
方法和属性(Methods and properties)
可以在 case 中定义方法
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 下定义方法
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"
可变方法(Mutating Methods)
enum 中可以定义可变方法,这样就可以更改 self 的值
enum TriStateSwitch {
case Off, Low, High
mutating func next() {
switch self {
case Off:
self = Low
case Low:
self = High
case High:
self = Off
}
}
属性(Properties)
enum不能定义存储属性,但是可以定义计算属性
使用自定义类型作为枚举的值
这个功能还是有很多地方需要用到的。
如果我们忽略关联值,则枚举的值就只能是整型,浮点型,字符串和布尔类型。如果想要支持别的类型,则可以通过实现 StringLiteralConvertible 协议来完成,这可以让我们通过对字符串的序列化和反序列化来使枚举支持自定义类型。
作为一个例子,假设我们要定义一个枚举来保存不同的 iOS 设备的屏幕尺寸:
enum Devices: CGSize {
case iPhone3GS = CGSize(width: 320, height: 480)
case iPhone5 = CGSize(width: 320, height: 568)
case iPhone6 = CGSize(width: 375, height: 667)
case iPhone6Plus = CGSize(width: 414, height: 736)
}
然而,这段代码不能通过编译。因为 CGPoint 并不是一个常量,不能用来定义枚举的值。我们需要为想要支持的自定义类型增加一个扩展,让其实现 StringLiteralConvertible 协议。这个协议要求我们实现三个构造方法,这三个方法都需要使用一个String类型的参数,并且我们需要将这个字符串转换成我们需要的类型(此处是CGSize)。
extension CGSize: StringLiteralConvertible {
public init(stringLiteral value: String) {
let size = CGSizeFromString(value)
self.init(width: size.width, height: size.height)
}
public init(extendedGraphemeClusterLiteral value: String) {
let size = CGSizeFromString(value)
self.init(width: size.width, height: size.height)
}
public init(unicodeScalarLiteral value: String) {
let size = CGSizeFromString(value)
self.init(width: size.width, height: size.height)
}
}
现在就可以来实现我们需要的枚举了,不过这里有一个缺点:初始化的值必须写成字符串形式,因为这就是我们定义的枚举需要接受的类型(记住,我们实现了 StringLiteralConvertible,因此String可以转化成CGSize类型)
enum Devices: CGSize {
case iPhone3GS = "{320, 480}"
case iPhone5 = "{320, 568}"
case iPhone6 = "{375, 667}"
case iPhone6Plus = "{414, 736}"
}
终于,我们可以使用 CGPoint 类型的枚举了。需要注意的是,当要获取真实的 CGPoint 的值的时候,我们需要访问枚举的是 rawValue 属性。
let a = Devices.iPhone5
let b = a.rawValue
print("the phone size string is \(a), width is \(b.width), height is \(b.height)")
// prints : the phone size string is iPhone5, width is 320.0, height is 568.0
使用字符串序列化的形式,会让使用自定义类型的枚举比较困难,然而在某些特定的情况下,这也会给我们增加不少便利(比较使用NSColor / UIColor的时候)。不仅如此,我们完全可以对自己定义的类型使用这个方法。
对枚举的关联值进行比较
在通常情况下,枚举是很容易进行相等性判断的。一个简单的 enum T { case a, b } 实现默认支持相等性判断 T.a == T.b, T.b != T.a
然而,一旦我们为枚举增加了关联值,Swift 就没有办法正确地为两个枚举进行相等性判断,需要我们自己实现 == 运行符。
enum Trade {
case Buy(stock: String, amount: Int)
case Sell(stock: String, amount: Int)
}
func ==(lhs: Trade, rhs: Trade) -> Bool {
switch (lhs, rhs) {
case let (.Buy(stock1, amount1), .Buy(stock2, amount2))
where stock1 == stock2 && amount1 == amount2:
return true
case let (.Sell(stock1, amount1), .Sell(stock2, amount2))
where stock1 == stock2 && amount1 == amount2:
return true
default: return false
}
}
正如我们所见,我们通过 switch 语句对两个枚举的 case 进行判断,并且只有当它们的 case 是匹配的时候(比如 Buy 和 Buy)才对它们的真实关联值进行判断。
自定义构造方法
enum Device {
case AppleWatch
static func fromSlang(term: String) -> Device? {
if term == "iWatch" {
return .AppleWatch
}
return nil
}
}
枚举的局限性
提取关联值
David Owens写过一篇文章,他觉得当前的关联值提取方式是很笨重的。我墙裂推荐你去看一下他的原文,在这里我对它的要旨进行下说明:为了从一个枚举中获取关联值,我们必须使用模式匹配。然而,关联值就是关联在特定枚举 case 的高效元组。而元组是可以使用更简单的方式来获取它内部值,即 .keyword 或者 .0
。