返回

监听属性,优雅应对自定义KVO!

IOS

自定义 KVO:轻松监视对象属性变化

简介

在 Swift 中,KVO(键值观察)是监视对象属性变化的一种强大机制。通过使用 @objc dynamic 修饰属性并添加观察者,可以方便地接收属性值变化的通知。然而,系统提供的 KVO 存在一些限制,包括手动释放观察者和区分不同属性的繁琐性。

本文将介绍如何创建自定义 KVO,解决这些限制,并提供更灵活、更方便的属性值变化监视解决方案。

自定义 KVO 的核心思想

自定义 KVO 的核心思想是使用 NSHashTable 存储观察者。当观察者被添加到自定义 KVO 时,它将被添加到 NSHashTable 中。当被监视的属性发生变化时,自定义 KVO 会遍历 NSHashTable,并调用每个观察者的回调函数。

这种方法解决了以下问题:

  • 避免内存泄漏: NSHashTable 使用弱引用存储观察者,当观察者被释放时,它会自动从表中移除,避免内存泄漏。
  • 无需区分不同属性: 自定义 KVO 在回调函数中使用 keyPath 参数,可以轻松区分不同属性的变化,而无需添加额外的 if 判断。

实现自定义 KVO

class CustomKVO {

    private let hashTable = NSHashTable<AnyObject>(options: .weakMemory)

    func addObserver(_ observer: AnyObject, forKeyPath keyPath: String) {
        hashTable.add(observer)
    }

    func removeObserver(_ observer: AnyObject) {
        hashTable.remove(observer)
    }

    func notifyObservers(forKeyPath keyPath: String, newValue: Any?) {
        for observer in hashTable.allObjects {
            observer.observeValue(forKeyPath: keyPath, of: self, change: [NSKeyValueChangeKey.newKey: newValue], context: nil)
        }
    }
}

用法示例:

let customKVO = CustomKVO()

customKVO.addObserver(self, forKeyPath: "name")
customKVO.addObserver(self, forKeyPath: "age")

name = "John Doe"
age = 30

// 观察者收到通知
// 输出:name changed to John Doe
// 输出:age changed to 30

自定义 KVO 的优势

自定义 KVO 具有以下优势:

  • 自动释放观察者: 避免内存泄漏,简化观察者管理。
  • 无需区分不同属性: 简化回调函数,提高代码可读性和可维护性。
  • 支持多属性监听: 可以方便地监视多个属性的变化,无需重复添加观察者。

自定义 KVO 的限制

自定义 KVO 也存在一些限制:

  • 只能监视 @objc dynamic 属性: 与系统 KVO 一致,只能监视用 @objc dynamic 修饰的属性。
  • 无法监视 struct 和 enum 类型属性: Swift 中的结构体和枚举类型不支持 KVO。

结论

自定义 KVO 是一个轻量级、易于使用且功能强大的 KVO 实现,它解决了系统 KVO 的限制,提供了自动释放观察者、无需区分不同属性和支持多属性监听等优势。如果你需要在项目中监视对象属性变化,自定义 KVO 是一个不错的选择。

常见问题解答

  1. 自定义 KVO 如何与系统 KVO 比较?
    自定义 KVO 解决了系统 KVO 中手动释放观察者和区分不同属性的繁琐性,同时提供了类似的功能。

  2. 我可以在 Swift 中使用自定义 KVO 来监视所有属性吗?
    否,自定义 KVO 只能监视用 @objc dynamic 修饰的属性。

  3. 自定义 KVO 是否支持多属性监听?
    是的,自定义 KVO 支持同时监视多个属性的变化。

  4. 为什么自定义 KVO 使用 NSHashTable 来存储观察者?
    NSHashTable 使用弱引用存储观察者,防止内存泄漏,并简化了观察者管理。

  5. 自定义 KVO 是否存在性能开销?
    自定义 KVO 的性能开销通常较低,但性能影响可能取决于被监视属性的数量和观察者的数量。