Swift 中的枚举

159 阅读3分钟

简介

在 Swift 中,enum(枚举)是值类型,用于定义一组相关值,并且可以为这些值赋予关联值、原始值,甚至添加方法和计算属性。相比于 C 语言中的简单枚举,Swift 的枚举功能更强大,接近“代数数据类型”。

基本语法

定义枚举

关键字enum、case

enum Direction {
    case north
    case south
    case east
    case west
}

简写
enum Direction {
    case north, south, east, west
}


如:
enum RequestType{
    case get
    case post 
}

配合switch case使用

let dir = Direction.north

switch dir {
case .north:
    print("Going North")
case .south:
    print("Going South")
default:
    print("Going Somewhere")
}

switch 在 Swift 中必须穷尽所有情况,否则必须加 default

关联值(Associated Values)

可以为枚举成员携带额外信息。

enum Barcode {
    case upc(Int, Int, Int, Int)
    case qrCode(String)
}


var code = Barcode.upc(8, 85909, 51226, 3)

switch code {
case .upc(let a, let b, let c, let d):
    print("UPC: \(a)-\(b)-\(c)-\(d)")     // 取其中携带的 额外信息
case .qrCode(let str):
    print("QR Code: \(str)")
}

原始值(Raw Values)

为每个成员赋固定的值(只能是 String、Int、Double 等字面量)

(原始值不写的话默认是枚举值)

enum Grade: String {
    case perfect = "A"
    case great = "B"
    case good = "C"
    case bad = "D"
}
print(Grade.perfect)            // 枚举值:perfect
print(Grade.perfect.rawValue)   // 原始值:A 
  • 如果枚举的原始值类型是Int、String,Swift会自动分配原始值
enum Season: Int {
    case spring, summer, autumn, winter
}
print(Season.spring.rawValue)    // 0
print(Season.summer.rawValue)    // 1
print(Season.autumn.rawValue)    // 2
print(Season.winter.rawValue)    // 3

添加方法和计算属性

enum Direction {
    case north, south, east, west

    func isVertical() -> Bool {
        return self == .north || self == .south
    }
    
    var description: String {
        switch self {
        case .north: return "↑"
        case .south: return "↓"
        case .east:  return "→"
        case .west:  return "←"
    }
}

CaseIterable

令枚举遵循 CaseIterable 协议。Swift 会为其生成一个 allCases 属性,表示一个包含枚举所有成员的集合。下面是一个例子:

enum Beverage: CaseIterable {
    case coffee, tea, juice
}

let numberOfChoices = Beverage.allCases.count 

for beverage in Beverage.allCases {
    print(beverage)
} 

Equatable / Comparable

Swift 默认对没有关联值的 enum 自动实现 Equatable。你可以手动实现 Comparable

enum Rank: Int, Comparable {
    case two = 2, three, four, five, six

    static func < (lhs: Rank, rhs: Rank) -> Bool {
        return lhs.rawValue < rhs.rawValue
    }
}

嵌套枚举

struct Chess {
    enum Piece {
        case king, queen, bishop
    }
}

let piece = Chess.Piece.king

带泛型的枚举

Swift 标准库中大量使用泛型枚举

enum Result<Success, Failure: Error> {
    case success(Success)
    case failure(Failure)
}

与 Codable 搭配使用

当使用关联值时,实现 Codable 需要自定义 init(from:)encode(to:)

enum ServerResponse: Codable {
    case success(message: String)
    case failure(code: Int)
    
    enum CodingKeys: String, CodingKey {
        case type, message, code
    }
    
    enum ResponseType: String, Codable {
        case success, failure
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let type = try container.decode(ResponseType.self, forKey: .type)
        switch type {
        case .success:
            let message = try container.decode(String.self, forKey: .message)
            self = .success(message: message)
        case .failure:
            let code = try container.decode(Int.self, forKey: .code)
            self = .failure(code: code)
        }
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        switch self {
        case .success(let message):
            try container.encode(ResponseType.success, forKey: .type)
            try container.encode(message, forKey: .message)
        case .failure(let code):
            try container.encode(ResponseType.failure, forKey: .type)
            try container.encode(code, forKey: .code)
        }
    }
}

递归枚举

枚举值包含自己枚举,需要使用关键字indirect

indirect enum ArithExpr {
case number(Int)
case sum(ArithExpr, ArithExpr)
case difference(ArithExpr, ArithExpr)
}

let five = ArithExpr.number(5)
let four = ArithExpr.number(4)
let two = ArithExpr.number(2)
let sum = ArithExpr.sum(five, four)
let difference = ArithExpr.difference(sum, two) 

可以使用MemoryLayout获取数据类型占用的内存大小

 enum Password {
    case number(Int, Int, Int, Int)
    case other
}

MemoryLayout<Password>.stride // 40, 分配占用的空间大小 
MemoryLayout<Password>.size // 33, 实际用到的空间大小 
MemoryLayout<Password>.alignment // 8, 对齐参数