6-15.【OC】【KVC/KVO】在现代项目中,如果想替代 KVO,有哪些可行方案?

5 阅读2分钟

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 以保证安全。