Swift 中可以使用键值观察 (KVO) 吗?


在 Swift 中,您可以使用 KVO 通过为该属性注册观察者来观察对象属性的更改。当属性值更改时,会通知观察者,并可以采取适当的操作。在本文中,您将看到如何在 Swift 中实现 KVO 的示例。

要在 Swift 中使用 KVO,您需要执行以下操作

  • 使用 @objc dynamic 属性标记要观察的属性。此属性告诉 Swift 编译器为该属性生成与 Objective-C 兼容的代码。

  • 使用被观察对象的 addObserver(_:forKeyPath:options:context:) 方法注册该属性的观察者。

  • 在观察者中实现 observeValue(forKeyPath:of:change:context:) 方法以处理对被观察属性的更改。

addObserver(_:forKeyPath:options:context:) 方法

addObserver(_:forKeyPath:options:context:) 方法用于为对象的特定键路径添加观察者。它采用以下参数:

  • observer - 观察键路径的对象。

  • keyPath - 指定要观察的键路径的字符串。

  • options - 确定如何向观察者报告键路径更改的选项数组。

  • context - 当通知观察者更改时传递给观察者的上下文对象。

options 参数是一个选项数组,用于确定如何将对被观察键路径的更改报告给观察者。可用的选项包括:

  • .old - 在报告更改时,在更改字典中包含属性的旧值。

  • .new - 在报告更改时,在更改字典中包含属性的新值。

  • .initial - 在添加观察者时报告属性的初始值。

  • .prior - 在报告更改时,在更改字典中包含属性的上一个值。

context 参数是任意上下文对象,当通知观察者更改时传递给观察者。这可用于向观察者提供其他信息。

removeObserver(_:forKeyPath:) 方法

removeObserver(_:forKeyPath:) 方法用于删除对象的特定键路径的观察者。它采用以下参数:

  • observer - 观察键路径的对象。

  • keyPath - 指定停止观察的键路径的字符串。

当不再需要观察者时,必须将其移除。这是为了避免内存泄漏并防止观察者在其被释放后接收通知。

@objc dynamic

@objc dynamic 是 Swift 语言属性,用于使类的属性在 Objective-C 中可以通过键值观察 (KVO) 进行观察。KVO 是一种允许对象观察另一个对象属性的更改的机制。

@objc 属性用于将 Swift 类或其成员公开给 Objective-C,以便 Objective-C 代码可以使用它们。dynamic 属性用于告诉编译器对属性或方法使用动态调度而不是静态调度。这意味着属性或方法可以在运行时被重写。

为了使属性可以通过 KVO 进行观察,它必须同时使用 @objc 和 dynamic 进行标记。当属性使用 @objc dynamic 标记时,编译器会生成其他代码,从而为该属性启用 KVO。这包括创建一个新的动态子类,该子类重写属性的 getter 和 setter 方法,以便在属性值更改时通知注册的观察者。

示例

import Foundation
class Person: NSObject {
   // Declare the property that you want to observe as @objc dynamic in the class where it is defined.
   @objc dynamic var name: String = ""
}
// Define a new observer class that inherits from NSObject.
class PersonObserver: NSObject {
   var person: Person
   init(person: Person) {
      self.person = person
      super.init()
      person.addObserver(self, forKeyPath: #keyPath(Person.name), options: [.old, .new], context: nil)
   }
   deinit {
      person.removeObserver(self, forKeyPath: #keyPath(Person.name))
   }
   override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
      guard keyPath == #keyPath(Person.name),
      let newValue = change?[.newKey] as? String,
      let oldValue = change?[.oldKey] as? String else {
         return
      }
      print("Property '\(keyPath!)' changed from '\(oldValue)' to '\(newValue)'")
   }
}
// Create an instance of Person and an instance of PersonObserver.
let alex = Person()
let observer = PersonObserver(person: alex)
// Change the value of the name on alex
alex.name = "Alex"
alex.name = "Alex Murphy"

输出

Property 'name' changed from '' to 'Alex'
Property 'name' changed from 'Alex' to 'Alex Murphy'

在此示例中,Person 是被观察的对象,PersonObserver 是观察者。Person 的 name 属性使用 @objc dynamic 属性进行标记以启用 KVO。观察者为 alex 的 name 属性注册,并实现 observeValue(forKeyPath:of:change:context:) 方法以处理对该属性的更改。当属性值更改时,会调用观察者的方法实现,并传入更改后的值。

结论

总之,@objc dynamic 是 Swift 语言属性,用于使类的属性在 Objective-C 中可以通过键值观察 (KVO) 进行观察。为了使属性可以通过 KVO 进行观察,必须同时使用 @objc 和 dynamic 进行标记。当属性使用 @objc dynamic 标记时,编译器会生成其他代码,从而为该属性启用 KVO。建议仅在属性需要在 Objective-C 中通过 KVO 进行观察时才使用 @objc dynamic,因为不必要地使用它可能会影响性能。

更新于:2023年5月4日

1K+ 次查看

启动你的职业生涯

完成课程获得认证

开始学习
广告