关联值枚举
本教程共 93 篇 · 第 48 篇 · 更新于 2026-08-08 · 约 7 分钟阅读
本节目标:读完你能给枚举成员携带不同类型的数据(关联值),并用 switch 或 if case 把数据解构出来。
1-1 为什么需要关联值
原始值对每个成员是固定的,但很多时候同一个成员每次携带的数据都不一样。比如商品条码:有的是一维 UPC 码(四个数字),有的是二维 QR 码(一串字符串)。它们都属于”条码”这个概念,但附加信息形态不同。这时就得用关联值(associated values)。
原始值像一个印死的标签,关联值则像一个随身带的口袋,每次装的东西可以不同。理解这一点,枚举才真正变得好用。更关键的是,不同成员口袋里的”东西种类”可以完全不一样:UPC 装四个整数,QR 装一个字符串,这种”同一枚举、不同形态”的能力,正是关联值相对于原始值最大的优势。
1-2 定义带关联值的枚举
关联值写在成员名后面的括号里,每个成员可以带不同类型:
enum Barcode {
case upc(Int, Int, Int, Int)
case qrCode(String)
}
读法:Barcode 要么是 upc,带着四个 Int;要么是 qrCode,带着一个 String。定义时并不填具体数字,只是声明”将来能装什么类型”。括号里的类型列表,相当于给这个成员定了一个”数据模板”。
1-3 创建与使用
创建时给出具体数据:
var productBarcode = Barcode.upc(8, 85909, 51226, 3)
productBarcode = .qrCode("ABCDEFGHIJKLMNOP")
注意一个变量同一时刻只能存一种成员及其关联值。把 productBarcode 改成二维码后,原来的 UPC 数据就被替换掉了。
Tip把带关联值的枚举赋值给变量后,再改成另一成员,旧数据整体被新数据顶替。这正是值类型的表现:变量里装的永远是”当下这一份完整值”。也正因为如此,你不用担心两个变量”共享”了同一份可变数据。
1-4 用 switch 解构关联值
配合 switch,可以在匹配成员的同时把关联值取出来。用 let 把值绑定成常量,var 绑成变量:
switch productBarcode {
case .upc(let numberSystem, let manufacturer, let product, let check):
print("UPC: \(numberSystem), \(manufacturer), \(product), \(check).")
case .qrCode(let productCode):
print("QR code: \(productCode).")
}
switch 在这里做了两件事:先判断是哪种成员,再把口袋里的数据按位置解出来、分别命名。这样你就能直接在分支里用这些名字,不必事后再去取值。
如果一组关联值全是 let(或全是 var),可以简写,把 let 提到前面:
switch productBarcode {
case let .upc(numberSystem, manufacturer, product, check):
print("UPC : \(numberSystem), \(manufacturer), \(product), \(check).")
case let .qrCode(productCode):
print("QR code: \(productCode).")
}
这种简写读起来更顺:case 后面一个 let,表示”这个成员的关联值我全要,且都当常量”。
1-5 用 if case 只匹配一种
只想判断是不是某一种成员、顺手取数据,不一定要写完整 switch。用 if case 更轻量:
if case .qrCode(let productCode) = productBarcode {
print("QR code: \(productCode).")
}
它的匹配逻辑和 switch 里那一分支完全一样。let 同样把关联值取成常量。当你的逻辑只关心一种情况、其余情况直接忽略时,if case 比写一个四五个分支的 switch 更清爽。
Note关联值让枚举像其他语言里的”可辨识联合""标签联合”或”变体”。一句话记忆:成员决定”是哪一类”,关联值决定”这一类还带了什么数据”。这种”带数据的标签”在表达状态机、解析 JSON、描述网络响应时非常顺手。
1-6 关联值 vs 原始值
这两者容易混,记住关键区别:原始值在定义枚举时就写死,永远不变;关联值在每次创建变量时才给,每次可以不同。原始值类型统一,关联值各成员类型可以不同。实际写代码,需要”固定标签”用原始值,需要”随实例变化的数据”用关联值。一个枚举不能同时用原始值和关联值,选定其一即可。
1-7 一个更贴近日常用法的模样
虽然本教程侧重语言知识点、不写完整项目,但看一个常见的”网络结果”枚举能帮你建立直觉:
enum NetworkResult {
case success(data: String)
case failure(code: Int)
}
let r = NetworkResult.success(data: "ok")
switch r {
case .success(let data):
print("拿到:\(data)")
case .failure(let code):
print("失败码:\(code)")
}
成功时带数据、失败时带错误码,两种形态共用一个类型,比用两个独立变量”结果 + 错误码”清晰得多。
1-8 关联值类型可以混搭
前面提到 UPC 装四个 Int、QR 装一个 String,这已经展示了”不同成员类型不同”。更进一步,同一成员的多个关联值也能是不同类型,比如 case point(x: Int, y: Int, label: String) 就同时带整数和字符串。Swift 对关联值类型的唯一要求是”写清楚”,没有别的限制,自由度很高。
这种混搭在处理真实数据时特别有用。比如一个”消息”枚举,文本消息带 String,图片消息带(Data, 尺寸),系统消息只带一个 Int 错误码——三种成员各自带着对自己有意义的数据,类型系统保证你取数据时拿到的就是对应的类型,不会张冠李戴。
1-9 该不该用关联值
一个简单的判断:如果你发现一组状态,除了”是哪一种”之外,每种还附带不一样结构的数据,那关联值就是天然之选。反过来,如果所有成员共享同一份固定映射(比如网络返回的字符串恰好等于成员名),用原始值更轻。
拿不准时记住:关联值表达”随实例变化的数据”,原始值表达”写死不变的标签”。这个分界清楚了,枚举该长什么样基本就定下来了。
1-10 关联值不是元组
虽然关联值写起来像元组,但本质不同:元组只是一组值的打包,没有”这是哪一种”的语义;关联值则挂在某个具体成员下,取数据时编译器知道你是哪种情况。正因如此,switch 解构关联值时能同时确认”类型”和”数据”,安全性远高于裸元组。
最后再提醒一句:一个枚举要么用原始值、要么用关联值,二者不能在同一枚举里混用。选定哪一种,取决于你的数据是”写死的标签”还是”随实例变的数据”。
1-11 小结
关联值让枚举成员能附带数据,且各成员类型可不同。定义时括号声明类型,使用时填具体值。switch 用 let/var 解构提取,全部同前缀可简写;只关心一种成员时用 if case。它和原始值的核心区别是”固定 vs 可变”。下一章我们看枚举如何描述递归结构。