1. 原生首选:Swift 属性观察器 (didSet / willSet)
这是最简单、性能最高,也是最符合 Swift 直觉的方案。
- 适用场景: 类内部逻辑、简单的组件间通信。
- 优点: 强类型检查、不需要继承
NSObject、无需手动解绑。 - 局限性: 只能在定义属性的类内部编写。如果外部想要监听,通常需要配合闭包。
2. 现代响应式标准:Combine / Swift Data (Observable)
如果你在做 数据绑定,这是苹果目前力推的方案。
Combine (@Published)
-
原理: 通过属性包装器将属性变为一个“发布者”。
-
优势: 支持链式操作(如
filter,debounce等),非常适合处理搜索输入或复杂的异步流。 -
代码示例:
Swift
class UserViewModel { @Published var name: String = "" } // 外部监听 viewModel.$name.sink { newName in print(newName) }.store(in: &cancellables)
Swift 5.9+ @Observable 宏
这是 SwiftUI 的未来。它彻底消灭了订阅代码,视图会自动追踪它读过的属性。
- 适用场景: 纯 Swift 项目,特别是 SwiftUI 架构。
- 优势: 零样板代码,性能极佳。
3. 轻量级闭包回调 (Boxing)
在 MVVM 中,如果你不想引入庞大的 Combine 框架,可以使用一个简单的“包装类”。
-
原理: 定义一个泛型类,在
didSet时执行存储的闭包。 -
代码示例:
Swift
class ObservableValue<T> { var value: T { didSet { listener?(value) } } private var listener: ((T) -> Void)? func bind(_ listener: @escaping (T) -> Void) { self.listener = listener listener(value) } } -
优点: 极其轻量,逻辑透明,不依赖任何系统 API。
4. 传统委派模式 (Delegate)
不要觉得 Delegate 过时,它依然是一对一通信中最稳健的方案。
- 适用场景: 严格的父子组件通信,需要明确定义接口。
- 优势: 避免了 KVO 这种“暗箱操作”,代码可读性极强,追踪调用栈非常简单。
5. 通知中心 (NotificationCenter)
- 适用场景: 一对多、跨模块的松耦合通信(例如:用户登录状态改变)。
- 缺点: 字符串硬编码多,传递数据需要装箱成
Dictionary,类型安全较差。
方案选型对比表
| 方案 | 耦合度 | 类型安全 | 复杂度 | 推荐等级 |
|---|---|---|---|---|
didSet | 低 | 高 | 极简 | ⭐⭐⭐⭐⭐ |
| Combine | 中 | 高 | 较高 | ⭐⭐⭐⭐ (MVVM 必备) |
@Observable | 极低 | 极高 | 简便 | ⭐⭐⭐⭐⭐ (iOS 17+) |
| Delegate | 高 | 高 | 中等 | ⭐⭐⭐⭐ |
| KVO | 中 | 低 | 高 | ⭐ (仅限监听系统 SDK) |
💡 核心建议
- 如果是内部属性变更:用
didSet。 - 如果是 SwiftUI 视图绑定:用
@Observable(iOS 17) 或ObservableObject(iOS 13+)。 - 如果是复杂的跨页面数据流:用 Combine。
- 如果是老旧 OC 代码混编:才考虑继续用 KVO,但务必使用 iOS 11+ 的闭包版本 API 以保证安全。