XiaoboTalk

Enum 成为 Swfit 语言的一条独立设计分支

Swift 的枚举设计,比较能体现这门语言的设计哲学:
让类型系统表达程序的真实意图,用编译器保证安全,用枚举表达可能性空间
更加清晰和细致的类型系统,是现代编程语言的发展趋势,swift 中 enum 被视为一等公民。足见其重要性,很多 Swift 语言的基础设施都是通过 enum 来提炼类型系统实现的。
1、例如 swift 中的 Optional 的实现就是个枚举:
enum Optional<Wrapped> { case none case some(Wrapped) }
这个泛型枚举,可以看做为一个类型集合:{ none, some(关联值 wrapped) },有了这个类型集合,编译期就可以对每个编译期的值进行类型推断和检测。这就是让编译期用类型系统保证安全。
2、还有 Result 也是通过枚举实现的
enum Result<Success, Failure: Error> { case success(Success) case failure(Failure) }
可见 Swift 中的 enum 很重要,可以用枚举实现很多基础实现。

Enum 分类

为了方便理解和记忆,swift 的 enum 设计可以分为 3 类:
  1. 状态集合枚举 (标签枚举、无原始值枚举), Tag Enum
  1. 有原始值的枚举 RawValue Enum
  1. 关联值枚举 Associated Enum;带标签的联合体(tagged union),一种变体 variant。
其中 rawValue Enum associated Enum 属于不同的设计范畴,一个枚举不能同时拥有原始值和关联值。

1、Tag Enum

就是最普通的枚举,没有原始值,仅定义一组有限的状态,编译期决定好类型:
enum Direction { case up, down, left, right }
在底层其实是一个整型 tag (猜测):
// 伪代码 struct Direction { enum Tag { up, down, left, right } Tag _tag; }
这种枚举可以类比为 javascript 中的 Symbol 类型

2、RawValue Enum

这种是值映射型,相当于对 C 语言等整型枚举的一个扩展。
enum Direction: String { case up = "U" case down = "D" }
底层编译期生成伪代码:
struct Direction { enum Tag { up, down } Tag _tag; static let rawValues = ["U", "D"] }
这种枚举可以做更灵活的语义化表达,某些场景用来提升代码可读性。RawValue 枚举的另一个好处,可以通过一个 rawValue 初始化一个枚举,不过初始化的是一个可选 Optional 类型的值
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:
enum Result<Success, Failure> where Failure: Error { case success(Success) case failure(Error) }
结果从枚举层面看,可以分为两种状态:成功、失败。但实际情况是,成功状态下还需要处理对应的数据,这个数据就是这种状态下的关联值。这样既能使用枚举做类型限定,同时还能表达数据。 枚举的底层实现类似 “带标签的联合体”:
enum Direction { case move(x: Int, y: Int) case stop }
底层模型(伪 C++):
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、使用场景总结

类型
场景
示例
基本枚举
固定状态集合
enum Direction { case up, down }
原始值枚举
映射外部值(API、持久化、枚举名序列化)
enum HTTPMethod: String { case get="GET", post="POST" }
关联值枚举
表达变体(variant)类型或结果类型
enum Result { case success(Data), failure(Error) }
这三种写法其实体现了 Swift 的三个设计理念:
枚举类型
设计哲学
关键词
基本枚举
用类型表示状态空间
安全、显式
原始值枚举
类型安全的值映射
序列化、桥接外部系统
关联值枚举
代数数据类型(ADT)
表达力、可组合性

获取原始值和关联值

1、获取原始值

Enum 有原生支持的属性 rawValue
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 解包来获取值
switch case2 { case .DevTest(let a, let b): print("DevTest 参数:", a, b) case .ProdTest(let str): print("ProdTest 参数:", str) }
2.2 使用 if case 解构(适合单一判断)
if case let .ProdTest(env) = case3 { print("当前环境:", env) }

值类型的 enum 属性支持

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

无论 enum 内部放的是什么,enum 本身是值类型。这个含义是,enum 赋值的时候,都是值拷贝。这是由语言层级(编译器定义)决定的,不会因为它内部“引用”了类实例而改变。
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 的内存结构不再可预测。
enum TestEnum { var name: String = "Hello" // ❌ 编译错误 }

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

计算属性:通过计算返回,不占用额外的存储空间
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
静态属性用于枚举级别的共享信息,不依赖具体枚举实例。
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)