Swift 的枚举设计,比较能体现这门语言的设计哲学:

让类型系统表达程序的真实意图,用编译器保证安全,用枚举表达可能性空间

更加清晰和细致的类型系统,是现代编程语言的发展趋势,swift 中 enum 被视为一等公民。足见其重要性,很多 Swift 语言的基础设施都是通过 enum 来提炼类型系统实现的。

1、例如 swift 中的 Optional 的实现就是个枚举:

swift
enum Optional<Wrapped> {
    case none
    case some(Wrapped)
}

这个泛型枚举,可以看做为一个类型集合:{ none, some(关联值 wrapped) },有了这个类型集合,编译期就可以对每个编译期的值进行类型推断和检测。这就是让编译期用类型系统保证安全。

2、还有 Result 也是通过枚举实现的

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

可见 Swift 中的 enum 很重要,可以用枚举实现很多基础实现。

Enum 分类

为了方便理解和记忆,swift 的 enum 设计可以分为 3 类:

  1. 状态集合枚举 (标签枚举、无原始值枚举), Tag Enum
  2. 有原始值的枚举 RawValue Enum
  3. 关联值枚举 Associated Enum;带标签的联合体(tagged union),一种变体 variant。

其中 rawValue Enum 和 associated Enum 属于不同的设计范畴,一个枚举不能同时拥有原始值和关联值。

1、Tag Enum

就是最普通的枚举,没有原始值,仅定义一组有限的状态,编译期决定好类型:

swift
enum Direction { case up, down, left, right }

在底层其实是一个整型 tag (猜测):

swift
// 伪代码
struct Direction {
    enum Tag { up, down, left, right }
    Tag _tag;
}

这种枚举可以类比为 javascript 中的 Symbol 类型。

2、RawValue Enum

这种是值映射型,相当于对 C 语言等整型枚举的一个扩展。

swift
enum Direction: String {
    case up = "U"
    case down = "D"
}

底层编译期生成伪代码:

swift
struct Direction {
    enum Tag { up, down }
    Tag _tag;
    static let rawValues = ["U", "D"]
}

这种枚举可以做更灵活的语义化表达,某些场景用来提升代码可读性。RawValue 枚举的另一个好处,可以通过一个 rawValue 初始化一个枚举,不过初始化的是一个可选 Optional 类型的值

swift
enum TestCaseAgency {
    case DevTest
    case ProdTest
}

let t1: TestCaseRaw? = TestCaseRaw.init(rawValue: "DevTest");
let t2: TestCaseRaw? = TestCaseRaw.init(rawValue: "emp");

if let r1 = t1 {
    print("r1: ", r1.rawValue);
}

if let _ = t2 {
    
} else {
    print("empty t2");
}

3、Associated Enum

关联值,目的是可以额外绑定数据,让某种状态下的表现能力更强。例如系统的 Result:

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

结果从枚举层面看,可以分为两种状态:成功、失败。但实际情况是,成功状态下还需要处理对应的数据,这个数据就是这种状态下的关联值。这样既能使用枚举做类型限定,同时还能表达数据。 枚举的底层实现类似 “带标签的联合体”:

swift
enum Direction {
    case move(x: Int, y: Int)
    case stop
}

底层模型(伪 C++):

swift
struct Direction {
    enum Tag { move, stop } _tag;
    union {
        struct { int x, y; } moveData;
    };
}

总结到这里,看看 swift 官方文档的描述:

“Enumerations like these are also known as discriminated unions, tagged unions, or variants.”

此外需要注意,关联值枚举,是个运行时类型,绑定的值需要运行时才能确认。

4、使用场景总结

Unsupported Notion block: table

这三种写法其实体现了 Swift 的三个设计理念:

Unsupported Notion block: table

获取原始值和关联值

1、获取原始值

Enum 有原生支持的属性 rawValue

swift
enum TestCaseRaw: String {
    case DevTest = "dev"
    case ProdTest = "prod"
}

let case1 = TestCaseRaw.DevTest
print(case1.rawValue)   // 输出: "dev"
  • .rawValue 是 Swift 自动生成的只读属性;
  • 只能用于定义了原始值类型(enum Name: Type {})的枚举;
  • 原始值在编译期固定(编译器直接分配表)。

2、获取关联值 2.1 使用 switch 模式匹配,let 解包来获取值

swift
switch case2 {
case .DevTest(let a, let b):
    print("DevTest 参数:", a, b)
case .ProdTest(let str):
    print("ProdTest 参数:", str)
}

2.2 使用 if case 解构(适合单一判断)

swift
if case let .ProdTest(env) = case3 {
    print("当前环境:", env)
}

值类型的 enum 属性支持

1、先明确,swift 的 enum 是值类型,值拷贝

无论 enum 内部放的是什么,enum 本身是值类型。这个含义是,enum 赋值的时候,都是值拷贝。这是由语言层级(编译器定义)决定的,不会因为它内部“引用”了类实例而改变。

swift
enum Container {
    case number(Int)
    case object(MyClass)
}

class MyClass {
    var value: Int
    init(_ v: Int) { self.value = v }
}

var a = Container.object(MyClass(10))
var b = a  // 值拷贝(enum 是值类型)

此时:

  • a 和 b 是两个独立的 枚举值
  • 但它们内部都“引用了”同一个 MyClass 实例

也就是说:

enum 的复制是“浅拷贝”语义上的值拷贝。

这与结构体的行为是一致的。

2、enum 内部支持的属性定义

2.1 不支持存储属性

因为 enum 是一种状态表示的集合,支持存储属性,会破坏状态的稳定性。枚举的本质是一个 有限离散状态的联合类型(sum type),每个 case 可能携带不同关联值。如果允许存储属性,就会破坏这个模型的“固定内存布局”特性,使得每个 case 的内存结构不再可预测。

swift
enum TestEnum {
    var name: String = "Hello" // ❌ 编译错误
}

2.2 支持计算属性和类型属性

计算属性:通过计算返回,不占用额外的存储空间

swift
enum Direction {
    case north, south, east, west

    var opposite: Direction {
        switch self {
        case .north: return .south
        case .south: return .north
        case .east: return .west
        case .west: return .east
        }
    }

    var description: String {
        switch self {
        case .north: return "↑ North"
        case .south: return "↓ South"
        case .east: return "→ East"
        case .west: return "← West"
        }
    }
}

let d = Direction.east
print(d.opposite)      // west
print(d.description)   // → East

静态属性用于枚举级别的共享信息,不依赖具体枚举实例。

swift
enum APIEnvironment {
    case dev, staging, prod

    static let defaultBaseURL = "https://api.example.com"

    var baseURL: String {
        switch self {
        case .dev: return "https://dev.api.example.com"
        case .staging: return "https://staging.api.example.com"
        case .prod: return APIEnvironment.defaultBaseURL
        }
    }
}

print(APIEnvironment.defaultBaseURL)
print(APIEnvironment.prod.baseURL)