Swift 中的 Associated Values:什么是关联值、语法与示例

作者: IT Sectr 发布日期: 2026-06-17 阅读时间: 6 分钟

Associated Values — Swift 的一种机制,允许将任意数据单独附加到每个枚举变体。与原始值不同(所有情形具有相同类型的值),关联值可以在类型和数量上因情形而异。根据 Apple Documentation, 2026,这是在不创建额外包装类型的情况下精确建模领域的关键能力。

要点

  • Associated Values — 附加到枚举情形的单独数据
  • 类型 关联值可因每个情形而异
  • 提取 通过 switch 或 if case 中的值绑定实现
  • 互斥 — 关联值与原始值不兼容
  • 实践 — 建模错误、查询结果和 UI 状态

什么是关联值?

Associated Values — 是枚举情形可以随自身一起存储的附加信息。与原始值不同(原始值在声明级别固定),关联值在创建枚举实例时确定。

想象对支付方式的建模:creditCard 情形可以存储最后 4 位数字和有效期,而 applePay 情形则不存储任何内容。使用关联值,这可以自然地实现,无需子类或可选字段。

根据 Apple 文档,关联值将枚举从简单的常量集合转换为代数数据类型(和类型),这是函数式语言的特征。这显著提高了代码的表现力。

关联值的语法

Associated values 在情形后的圆括号中声明,指定参数名称和类型,如同函数一样。

swift
enum MediaFile {
    case image(width: Int, height: Int, format: String)
    case video(duration: Double, codec: String)
    case audio(bitrate: Int)
    case unknown
}

创建实例时,关联值作为参数传递:

swift
let photo = MediaFile.image(width: 1920, height: 1080, format: "jpeg")
let clip = MediaFile.video(duration: 120.5, codec: "h264")

Swift 不对关联值的数量或类型施加限制。您可以在关联值内部使用泛型、闭包函数和其他枚举。这使得可以构建复杂的嵌套结构。

关联值的参数名称在创建实例时用作参数标签——这提高了代码的可读性。通过 switch 提取时,名称充当文档,解释每个值的含义。这种方法使代码自文档化,并降低数据匹配时出错的可能性。这在大型 iOS 应用的团队开发中尤其有价值。

带关联值的 Switch

Switch — 提取关联值的主要方式。值通过 letvar 绑定到常量或变量。

swift
func describe(_ file: MediaFile) -> String {
    switch file {
    case .image(let w, let h, let fmt):
        return "Image \(w)x\(h) .\(fmt)"
    case .video(let duration, let codec):
        return "Video \(duration)s, \(codec)"
    case .audio(let bitrate):
        return "Audio \(bitrate)kbps"
    case .unknown:
        return "Unknown format"
    }
}

Swift 支持部分匹配——可以指定关联值的具体值:

swift
switch file {
case .image(let w, let h, "png"):
    print("PNG 图像 \(w)x\(h)")
case .image(1920, 1080, _):
    print("全高清图像")
default:
    break
}

使用通配符_)可以忽略不需要的关联值,而情形中的常量可以检查与特定值的匹配。

关联值与原始值的对比

许多初学者将关联值与原始值混淆。它们是具有不同应用场景的不同机制。

特性原始值关联值
数据类型所有情形使用同一类型每个情形可不同
确定时机枚举声明时创建实例时
必要性所有情形都有值某些情形可以没有值
兼容性互斥——不能同时使用

根据 Swift Evolution,关联值在 Swift 2.0 中作为枚举重新设计的一部分引入。它们提供了类型的代数方法,其中每个情形都是一个具有自己签名的独立构造器。

关联值的实际示例

关联值在实际的 iOS 项目中积极使用。让我们考虑三种典型场景。

建模 API 响应

swift
enum APIResponse<T: Codable> {
    case success(data: T, cached: Bool)
    case failure(error: Error, retryAvailable: Bool)
    case loading(progress: Double)
}

UI 状态机

swift
enum ViewState<T> {
    case idle
    case loading(message: String)
    case loaded(data: T)
    case error(message: String, retryAction: () -> Void)
}

使用关联值,枚举成为有限状态机的强大工具:每个情形——一种状态,关联值——该状态的参数。编译器保证所有转换都被处理。

通过 if case 和 guard case 提取

要在不使用switch的情况下检查一个情形,请使用 if caseguard case。当只需要处理一个变体时,这更简洁。

swift
let error = NetworkError.timeout(seconds: 30)

if case .timeout(let seconds) = error {
    print("请求在 \(seconds) 秒后超时")
}

guard case .httpError(let code, _) = error else {
    return
}
print("HTTP 错误,代码 \(code)")

常见问题

枚举是否能有无参数的关联值?

可以,情形可以没有关联值——例如 case unknown。这种情形像普通枚举情形一样行为,没有额外数据。这对于标记状态很方便。

如何比较两个带关联值的枚举?

Swift 不会自动为带关联值的枚举生成 Equatable(在 Swift 4.2+ 之前)。添加显式的 Equatable 实现,或使用 switch 逐一比较每个情形。

关联值可以在递归枚举中使用吗?

可以,indirect 枚举中的关联值是构建树和链表的标准技术。情形可以包含带有 indirect 标记的相同类型的关联值。

关联值与结构体中的可选属性有何区别?

关联值是类型安全的——每个情形只保证拥有自己的数据。在带有可选项的结构体中,每个实例包含所有字段,并且需要运行时检查 nil。

关联值是否支持 Codable?

Swift 不会自动为带关联值的枚举生成 Codable。需要使用自定义编码键通过 switch 对情形进行 encodedecode 的手动实现。

总结

  • 关联值 — 将不同数据附加到每个枚举情形的机制
  • 语法 — 在情形后的括号中声明参数,如同函数
  • 提取 通过使用 let 绑定的 switch 或 if case
  • 不兼容 原始值——使用其中之一
  • 泛型 在关联值中使枚举成为通用容器
  • 状态机 — iOS 中关联值的典型场景

我们将开发一款交钥匙移动应用程序

IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读