监听属性,优雅应对自定义KVO!
2023-12-29 01:38:53
自定义 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 是一个不错的选择。
常见问题解答
-
自定义 KVO 如何与系统 KVO 比较?
自定义 KVO 解决了系统 KVO 中手动释放观察者和区分不同属性的繁琐性,同时提供了类似的功能。 -
我可以在 Swift 中使用自定义 KVO 来监视所有属性吗?
否,自定义 KVO 只能监视用@objc dynamic
修饰的属性。 -
自定义 KVO 是否支持多属性监听?
是的,自定义 KVO 支持同时监视多个属性的变化。 -
为什么自定义 KVO 使用 NSHashTable 来存储观察者?
NSHashTable
使用弱引用存储观察者,防止内存泄漏,并简化了观察者管理。 -
自定义 KVO 是否存在性能开销?
自定义 KVO 的性能开销通常较低,但性能影响可能取决于被监视属性的数量和观察者的数量。