©️ OverlookArt
首页 / Swift / 枚举

枚举

概述

枚举 (Enumeration) 为定义一组相关值提供了一种强大的方式,使代码更易读、更易维护。Swift 中的枚举比许多其他语言中的枚举更加灵活。

1enum 枚举名称 {
2    case 案例1
3    case 案例2
4    // ...
5}

基本语法

定义枚举

1enum CompassPoint {
2    case north
3    case south
4    case east
5    case west
6}

简写形式

多个 case 可以写在同一行,用逗号分隔:

1enum CompassPoint {
2    case north, south, east, west
3}

创建和使用枚举

 1let direction = CompassPoint.north
 2
 3// 使用 switch 匹配
 4switch direction {
 5case .north:
 6    print("向北")
 7case .south:
 8    print("向南")
 9case .east:
10    print("向东")
11case .west:
12    print("向西")
13}

原始值 (Raw Values)

字符串原始值

1enum Planet: String {
2    case mercury = "Mercury"
3    case venus = "Venus"
4    case earth = "Earth"
5    case mars = "Mars"
6}
7
8let earth = Planet.earth
9print(earth.rawValue)  // 输出: "Earth"

整数原始值

1enum StatusCode: Int {
2    case success = 200
3    case notFound = 404
4    case serverError = 500
5}
6
7let status = StatusCode.success
8print(status.rawValue)  // 输出: 200

自动赋值

对于整数类型,如果没有指定值,Swift 会自动递增赋值:

1enum Planet: Int {
2    case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
3}
4
5print(Planet.venus.rawValue)  // 输出: 2
6print(Planet.earth.rawValue)  // 输出: 3

对于字符串类型,Swift 会自动使用 case 名称作为原始值:

1enum Direction: String {
2    case north, south, east, west
3}
4
5print(Direction.north.rawValue)  // 输出: "north"

从原始值创建枚举

1if let success = StatusCode(rawValue: 200) {
2    print("成功: \(success)")
3}
4
5if let notFound = StatusCode(rawValue: 404) {
6    print("未找到")
7}

关联值 (Associated Values)

关联值允许枚举的每个 case 携带额外的数据:

 1enum Barcode {
 2    case upc(Int, Int, Int, Int)
 3    case qrCode(String)
 4}
 5
 6let productCode = Barcode.qrCode("ABC123456")
 7
 8switch productCode {
 9case .upc(let numberSystem, let manufacturer, let product, let check):
10    print("UPC: \(numberSystem)-\(manufacturer)-\(product)-\(check)")
11case .qrCode(let code):
12    print("QR码: \(code)")
13}
14// 输出: QR码: ABC123456

简写形式

如果所有关联值都是常量或变量,可以用 letvar 统一声明:

1switch productCode {
2case let .upc(numberSystem, manufacturer, product, check):
3    print("UPC: \(numberSystem)-\(manufacturer)-\(product)-\(check)")
4case let .qrCode(code):
5    print("QR码: \(code)")
6}

switch 匹配与 if 判断

switch 基本用法

switch 语句用于匹配枚举的所有 case,必须覆盖所有情况或包含 default 分支:

 1enum Weather {
 2    case sunny(temperature: Double)
 3    case rainy(intensity: Double)
 4    case snowy(accumulation: Double)
 5}
 6
 7let currentWeather = Weather.sunny(temperature: 28.5)
 8
 9switch currentWeather {
10case .sunny(let temp):
11    print("晴天,温度 \(temp)°C")
12case .rainy(let intensity):
13    print("雨天,强度 \(intensity)")
14case .snowy(let accumulation):
15    print("雪天,积雪 \(accumulation)cm")
16}
17// 输出: 晴天,温度 28.5°C

where 子句

使用 where 子句添加额外的条件判断:

 1switch currentWeather {
 2case .sunny(let temp) where temp > 30:
 3    print("高温天气,注意防暑")
 4case .sunny(let temp) where temp < 10:
 5    print("寒冷天气,注意保暖")
 6case .sunny(let temp):
 7    print("晴天,温度 \(temp)°C")
 8case .rainy(let intensity):
 9    print("雨天,强度 \(intensity)")
10case .snowy(let accumulation):
11    print("雪天,积雪 \(accumulation)cm")
12}

隐式穷举

switch 语句覆盖枚举的所有 case 时,不需要 default 分支:

 1enum Direction {
 2    case north, south, east, west
 3}
 4
 5let dir: Direction = .north
 6
 7switch dir {
 8case .north: print("北")
 9case .south: print("南")
10case .east:  print("东")
11case .west:  print("西")
12}

if case 模式匹配

使用 if case 进行单分支模式匹配,比 switch 更简洁:

 1enum Status {
 2    case success(String)
 3    case failure(Int)
 4}
 5
 6let result: Status = .success("操作成功")
 7
 8// 匹配单个 case
 9if case .success(let message) = result {
10    print("成功: \(message)")
11}
12// 输出: 成功: 操作成功
13
14// 不匹配时不执行
15if case .failure(let code) = result {
16    print("失败: \(code)")
17}
18// 不打印任何内容

guard case 模式匹配

使用 guard case 在条件不匹配时提前退出:

 1func processResult(_ result: Status) {
 2    guard case .success(let message) = result else {
 3        print("处理失败")
 4        return
 5    }
 6    print("处理成功: \(message)")
 7}
 8
 9processResult(.success("完成"))  // 输出: 处理成功: 完成
10processResult(.failure(404))     // 输出: 处理失败

组合条件

if case 可以与其他条件组合:

 1let results: [Status] = [
 2    .success("成功1"),
 3    .failure(404),
 4    .success("成功2"),
 5    .failure(500)
 6]
 7
 8for result in results {
 9    if case .failure(let code) = result, code >= 500 {
10        print("服务器错误: \(code)")
11    }
12}
13// 输出: 服务器错误: 500

多 case 匹配

使用逗号分隔多个 case:

 1enum Shape {
 2    case circle(radius: Double)
 3    case rectangle(width: Double, height: Double)
 4    case triangle(base: Double, height: Double)
 5}
 6
 7let shape: Shape = .circle(radius: 5)
 8
 9switch shape {
10case .circle, .triangle:
11    print("曲线或三角形")
12case .rectangle:
13    print("矩形")
14}

提取多个关联值

 1enum Coordinate {
 2    case point(x: Double, y: Double)
 3    case line(start: (Double, Double), end: (Double, Double))
 4}
 5
 6let coord: Coordinate = .line(start: (0, 0), end: (10, 10))
 7
 8switch coord {
 9case .point(let x, let y):
10    print("点: (\(x), \(y))")
11case .line(let (x1, y1), let (x2, y2)):
12    print("线段: (\(x1),\(y1)) -> (\(x2),\(y2))")
13}

解包可选枚举

使用 if case 解包可选值:

 1enum Optional<T> {
 2    case some(T)
 3    case none
 4}
 5
 6let value: Optional<Int> = .some(42)
 7
 8if case .some(let wrapped) = value {
 9    print("有值: \(wrapped)")
10}
11// 输出: 有值: 42
12
13// 相当于
14if case Optional.some(let wrapped) = value {
15    print("有值: \(wrapped)")
16}

递归枚举

递归枚举是指枚举的 case 的关联值类型包含该枚举本身。使用 indirect 关键字声明:

 1indirect enum ArithmeticExpression {
 2    case number(Int)
 3    case addition(ArithmeticExpression, ArithmeticExpression)
 4    case multiplication(ArithmeticExpression, ArithmeticExpression)
 5}
 6
 7func evaluate(_ expression: ArithmeticExpression) -> Int {
 8    switch expression {
 9    case .number(let value):
10        return value
11    case .addition(let left, let right):
12        return evaluate(left) + evaluate(right)
13    case .multiplication(let left, let right):
14        return evaluate(left) * evaluate(right)
15    }
16}
17
18// 创建表达式: (2 + 3) * 4
19let expression = ArithmeticExpression.multiplication(
20    .addition(.number(2), .number(3)),
21    .number(4)
22)
23
24print(evaluate(expression))  // 输出: 20

也可以在特定 case 前添加 indirect

1enum Tree {
2    case empty
3    indirect case node(value: Int, left: Tree, right: Tree)
4}

枚举的方法和属性

实例属性

 1enum Weekday: String {
 2    case monday = "周一"
 3    case tuesday = "周二"
 4    case wednesday = "周三"
 5    case thursday = "周四"
 6    case friday = "周五"
 7    case saturday = "周六"
 8    case sunday = "周日"
 9    
10    var isWeekend: Bool {
11        switch self {
12        case .saturday, .sunday:
13            return true
14        default:
15            return false
16        }
17    }
18}
19
20print(Weekday.monday.isWeekend)  // 输出: false
21print(Weekday.saturday.isWeekend) // 输出: true

静态方法

 1enum Temperature {
 2    case celsius(Double)
 3    case fahrenheit(Double)
 4    
 5    static func fromCelsius(_ value: Double) -> Temperature {
 6        return .celsius(value)
 7    }
 8    
 9    static func fromFahrenheit(_ value: Double) -> Temperature {
10        return .fahrenheit(value)
11    }
12}
13
14let temp = Temperature.fromCelsius(25.0)

实例方法

 1enum Counter {
 2    case value(Int)
 3    
 4    mutating func increment() {
 5        if case .value(let current) = self {
 6            self = .value(current + 1)
 7        }
 8    }
 9    
10    mutating func increment(by amount: Int) {
11        if case .value(let current) = self {
12            self = .value(current + amount)
13        }
14    }
15}
16
17var counter = Counter.value(0)
18counter.increment()
19counter.increment(by: 5)

CaseIterable 协议

让枚举遵循 CaseIterable 协议,可以遍历所有 case:

 1enum Season: String, CaseIterable {
 2    case spring = "春"
 3    case summer = "夏"
 4    case autumn = "秋"
 5    case winter = "冬"
 6}
 7
 8for season in Season.allCases {
 9    print(season.rawValue)
10}
11// 输出: 春 夏 秋 冬

枚举初始化

原始值初始化

 1enum Color: String {
 2    case red = "红色"
 3    case green = "绿色"
 4    case blue = "蓝色"
 5    
 6    init?(colorName: String) {
 7        if let color = Color(rawValue: colorName) {
 8            self = color
 9        } else {
10            return nil
11        }
12    }
13}

关联值初始化

 1enum Result {
 2    case success(Int)
 3    case failure(String)
 4    
 5    init?(code: Int) {
 6        switch code {
 7        case 200...299:
 8            self = .success(code)
 9        default:
10            return nil
11        }
12    }
13}

协议遵循

枚举可以遵循多种协议:

 1// Equatable 自动合成
 2enum Status: Equatable {
 3    case active
 4    case inactive
 5    case pending
 6}
 7
 8// Codable
 9enum Response: Codable {
10    case data(String)
11    case error(Int)
12}
13
14// 自定义协议
15protocol Describable {
16    var description: String { get }
17}
18
19enum Shape: Describable {
20    case circle(radius: Double)
21    case rectangle(width: Double, height: Double)
22    
23    var description: String {
24        switch self {
25        case .circle(let r):
26            return "圆形,半径: \(r)"
27        case .rectangle(let w, let h):
28            return "矩形,宽: \(w),高: \(h)"
29        }
30    }
31}

常见用法示例

网络请求结果

 1enum NetworkResult {
 2    case success(data: Data)
 3    case failure(error: Error)
 4    case timeout(seconds: TimeInterval)
 5    case offline
 6}
 7
 8func handleResponse(_ result: NetworkResult) {
 9    switch result {
10    case .success(let data):
11        print("收到数据: \(data.count) 字节")
12    case .failure(let error):
13        print("请求失败: \(error.localizedDescription)")
14    case .timeout(let seconds):
15        print("请求超时: \(seconds) 秒")
16    case .offline:
17        print("无网络连接")
18    }
19}

可选值模拟

 1enum Optional<T> {
 2    case some(T)
 3    case none
 4}
 5
 6let value: Optional<Int> = .some(42)
 7
 8switch value {
 9case .some(let wrapped):
10    print("有值: \(wrapped)")
11case .none:
12    print("无值")
13}

枚举与类的区别

特性 枚举
0 实例创建 值类型 引用类型
1 继承 不支持 支持
2 存储属性 不支持 支持
3 计算属性 支持 支持
4 实例方法 支持(可修改 self) 支持
5 静态属性/方法 支持 支持

枚举与关联值的比较

特性 原始值 (Raw Values) 关联值 (Associated Values)
0 值类型 编译时确定 运行时确定
1 类型一致性 所有 case 相同类型 每个 case 可不同类型
2 用途 表示固定映射 表示动态数据
3 获取方式 rawValue 属性 switchif case 提取