您可以在 Swift 中使用 KVO,但只能用于 NSObject子类的 dynamic属性。假设您希望观察 Foo类的 bar属性。在 Swift 4中,将 bar指定为 NSObject子类中的 dynamic属性:
class Foo: NSObject {
@objc dynamic var bar = 0
}
然后可以注册以观察对 bar属性的更改。在 Swift 4和 Swift 3.2中,这已经被大大简化了,如 关键值观测在 Swift 中的应用所概述的:
class MyObject {
private var token: NSKeyValueObservation
var objectToObserve = Foo()
init() {
token = objectToObserve.observe(\.bar) { [weak self] object, change in // the `[weak self]` is to avoid strong reference cycle; obviously, if you don't reference `self` in the closure, then `[weak self]` is not needed
print("bar property is now \(object.bar)")
}
}
}
注意,在 Swift 4中,我们现在使用反斜杠字符强键入键路径(\.bar是所观察对象的 bar属性的键路径)。另外,因为它使用了完成闭包模式,我们不必手动删除观察者(当 token超出作用域时,观察者就被删除了) ,也不必担心如果键不匹配就调用 super实现。仅当调用这个特定的观察者时才调用闭包。有关更多信息,请参见 WWDC 2017视频,基金会最新消息。
在 Swift 3中,要观察到这一点,需要稍微复杂一些,但与 Objective-C 中的操作非常相似。也就是说,您将实现 observeValue(forKeyPath keyPath:, of object:, change:, context:),它(a)确保我们正在处理我们的上下文(而不是我们的 super实例已经注册要观察的内容) ; 然后(b)处理它或者在必要时将其传递给 super实现。并确保在适当的时候不要以旁观者的身份出现。例如,您可以在释放观察器时删除它:
雨燕3:
class MyObject: NSObject {
private var observerContext = 0
var objectToObserve = Foo()
override init() {
super.init()
objectToObserve.addObserver(self, forKeyPath: #keyPath(Foo.bar), options: [.new, .old], context: &observerContext)
}
deinit {
objectToObserve.removeObserver(self, forKeyPath: #keyPath(Foo.bar), context: &observerContext)
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
guard context == &observerContext else {
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
return
}
// do something upon notification of the observed object
print("\(keyPath): \(change?[.newKey])")
}
}
注意,您只能观察可以在 Objective-C 中表示的属性。因此,您不能观察泛型、 Swift struct类型、 Swift enum类型等。
[Note, this KVO discussion has subsequently been removed from the Using Swift with Cocoa and Objective-C guide, which has been adapted for Swift 3, but it still works as outlined at the top of this answer.]
It's worth noting that Swift has its own native property observer system, but that's for a class specifying its own code that will be performed upon observation of its own properties. KVO, on the other hand, is designed to register to observe changes to some dynamic property of some other class.
class MyObjectToObserve: NSObject {
var backing: NSDate = NSDate()
dynamic var myDate: NSDate {
set {
print("setter is called")
backing = newValue
}
get {
print("getter is called")
return backing
}
}
}
import Combine //Combine Framework
//Needs to be a class doesn't work with struct and other value types
class Car {
@Published var price : Int = 10
}
let car = Car()
//Option 1: Automatically Subscribes to the publisher
let cancellable1 = car.$price.sink {
print("Option 1: value changed to \($0)")
}
//Option 2: Manually Subscribe to the publisher
//Using this option multiple subscribers can subscribe to the same publisher
let publisher = car.$price
let subscriber2 : Subscribers.Sink<Int, Never>
subscriber2 = Subscribers.Sink(receiveCompletion: { print("completion \($0)")}) {
print("Option 2: value changed to \($0)")
}
publisher.subscribe(subscriber2)
//Assign a new value
car.price = 20
产出:
Option 1: value changed to 10
Option 2: value changed to 10
Option 1: value changed to 20
Option 2: value changed to 20