Swift KVO (Key-Value Observing)机制
Swift KVO (Key-Value Observing)机制

Swift KVO (Key-Value Observing)机制

KVO(Key-Value Observing)是一种基于对象属性变化的机制,它允许对象在属性发生变化时接收到通知。这个机制最初是由 Objective-C 提供的,后来也被引入到 Swift 中。KVO 使得对象能够观察其他对象的特定属性,并在属性值发生变化时执行相应的操作。

KVO 的工作原理

KVO 是通过观察者模式(Observer Pattern) 来实现的,允许一个对象(观察者)监听另一个对象(被观察者)属性的变化。当被观察者的属性发生变化时,观察者会收到通知。

主要步骤

1、观察者 需要注册到被观察者的特定属性。

2、被观察者 在属性发生变化时会通知所有注册的观察者。

3、观察者 根据通知执行某些操作,通常是更新界面或触发其他逻辑。

KVO 的使用

在 Objective-C 中,KVO 依赖于动态特性,通过动态派发机制来观察属性。为了让 KVO 工作,属性必须是动态的,因此需要在声明时使用 @objc dynamic,这也是 KVO 的关键。

示例:使用 KVO 监听属性变化

1、定义被观察者对象

import Foundation

class MyClass: NSObject {
    // 使用 @objc dynamic 使属性可以被 KVO 观察
    @objc dynamic var myProperty: String = "Initial Value"
}

@objc dynamic:

@objc 是将属性暴露给 Objective-C 运行时。

dynamic 告诉运行时该属性使用动态调度机制,使其能够被 KVO 观察。

2、定义观察者并注册观察

class ObserverClass: NSObject {
    var observedObject: MyClass
    
    init(observedObject: MyClass) {
        self.observedObject = observedObject
        super.init()
        // 注册观察者
        observedObject.addObserver(self, 
                                   forKeyPath: #keyPath(MyClass.myProperty), 
                                   options: [.new, .old], 
                                   context: nil)
    }
    
    // 观察到属性变化时会调用此方法
    override func observeValue(forKeyPath keyPath: String?, 
                               of object: Any?, 
                               change: [NSKeyValueChangeKey : Any]?, 
                               context: UnsafeMutableRawPointer?) {
        if keyPath == #keyPath(MyClass.myProperty) {
            if let newValue = change?[.newKey] {
                print("myProperty changed to \(newValue)")
            }
        }
    }
    
    deinit {
        // 移除观察者
        observedObject.removeObserver(self, forKeyPath: #keyPath(MyClass.myProperty))
    }
}

3、执行属性变化

let myObject = MyClass()
let observer = ObserverClass(observedObject: myObject)

myObject.myProperty = "New Value"  // 观察者会接收到通知

KVO 的工作流程

1、注册观察者:使用 addObserver(_:forKeyPath:options:context:) 方法将观察者注册到被观察者的特定属性。

2、观察属性变化:当被观察者的属性发生变化时,observeValue(forKeyPath:of:change:context:) 方法会被自动调用,通知观察者属性值发生了变化。

3、移除观察者:为了避免内存泄漏,观察者不再需要时,应该调用 removeObserver(_:forKeyPath:) 来移除观察者。

KVO 的核心方法

addObserver(_:forKeyPath:options:context:):注册观察者,指定要观察的属性。

removeObserver(_:forKeyPath:):移除观察者。

observeValue(forKeyPath:of:change:context:):当被观察属性发生变化时自动调用该方法,观察者在此方法中处理变化。

KVO 的选项

在注册观察时,可以指定一些选项(options),来控制观察者接收到哪些信息。

.new: 获取新值(变化后的值)。

.old: 获取旧值(变化前的值)。

.initial: 获取初始值(在第一次注册时会调用,尽管没有值变化)。

.prior: 获取变化前的值(在属性值变化之前的值)。

示例:KVO 和 change 字典

class ObserverClass: NSObject {
    var observedObject: MyClass
    
    init(observedObject: MyClass) {
        self.observedObject = observedObject
        super.init()
        observedObject.addObserver(self, 
                                   forKeyPath: #keyPath(MyClass.myProperty), 
                                   options: [.new, .old], 
                                   context: nil)
    }
    
    override func observeValue(forKeyPath keyPath: String?, 
                               of object: Any?, 
                               change: [NSKeyValueChangeKey : Any]?, 
                               context: UnsafeMutableRawPointer?) {
        if keyPath == #keyPath(MyClass.myProperty) {
            if let oldValue = change?[.oldKey] as? String {
                print("Old value: \(oldValue)")
            }
            if let newValue = change?[.newKey] as? String {
                print("New value: \(newValue)")
            }
        }
    }
}

这里,change 字典提供了属性变化的详细信息,包括新值(newKey)和旧值(oldKey)。

KVO 的局限性

1、性能问题:KVO 在处理大量属性变化时,可能会产生性能开销,因为它是基于动态消息派发的,可能会比较慢。

2、内存管理:如果观察者没有正确移除,可能会导致内存泄漏。这就是为什么在 deinit 中移除观察者是非常重要的。

3、难以调试:KVO 是基于运行时的特性,它没有显式的编译时错误提示,因此有时调试会变得困难。

KVO 在 Swift 中的使用

在 Swift 中,@objc dynamic 是启用 KVO 的必要条件。只有这样,属性才能通过动态派发被观察。

如果不使用 dynamic,Swift 会优化掉方法调用,导致无法实现 KVO。

总结

KVO 是一种观察对象属性变化的机制,它允许其他对象在属性值发生变化时收到通知。KVO 在许多框架中有广泛的应用,特别是在 Objective-C 和 Swift 混合编程 中。通过 addObserver(_:forKeyPath:options:context:) 方法可以注册观察者,通过 observeValue(forKeyPath:of:change:context:) 方法接收变化通知。KVO 的关键是确保被观察的属性是 @objc dynamic,以便能利用 Objective-C 运行时 的动态派发特性。

如果您认为这篇文章给您带来了帮助,您可以在此通过支付宝或者微信打赏网站开放者。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注