以下是我最初的 Swift 3 答案,但 Swift 4 简化了流程,无需任何转换。例如,如果您正在观察 Int 对象的 bar 属性 foo:
class Foo: NSObject {
@objc dynamic var bar: Int = 42
}
class ViewController: UIViewController {
let foo = Foo()
var token: NSKeyValueObservation?
override func viewDidLoad() {
super.viewDidLoad()
token = foo.observe(\.bar, options: [.new, .old]) { [weak self] object, change in
if change.oldValue != change.newValue {
self?.edit()
}
}
}
func edit() { ... }
}
注意,这种基于闭包的方法:
您唯一需要注意的是确保您的闭包不会引入强引用循环(因此使用[weak self] 模式)。
我原来的 Swift 3 答案如下。
你说:
但是,考虑到这一点,我认为这不适用于 swift 对象的原语,例如 Int,它(我假设)不会从 NSObject 继承,并且与 Objective-C 版本不同的是不会放入更改字典时,被装箱到NSNumber。
实际上,如果您查看这些值,如果观察到的属性是 Int,它确实会以 NSNumber 的形式出现在字典中。
所以,您可以留在NSObject 世界中:
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if let newValue = change?[.newKey] as? NSObject,
let oldValue = change?[.oldKey] as? NSObject,
!newValue.isEqual(oldValue) {
edit()
}
}
或将它们用作NSNumber:
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if let newValue = change?[.newKey] as? NSNumber,
let oldValue = change?[.oldKey] as? NSNumber,
newValue.intValue != oldValue.intValue {
edit()
}
}
或者,如果这是某个 Swift 类的某个 dynamic 属性的 Int 值,我会继续将它们转换为 Int:
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if let newValue = change?[.newKey] as? Int, let oldValue = change?[.oldKey] as? Int, newValue != oldValue {
edit()
}
}
你问:
另外,额外的问题,我如何使用of object 变量?它不会让我更改名称,当然也不喜欢其中有空格的变量。
of 是此参数的外部标签(在您调用此方法时使用;在这种情况下,操作系统会为我们调用此方法,因此我们不会在方法签名中使用此外部标签)。 object 是内部标签(在方法本身中使用)。 Swift 拥有为参数设置外部和内部标签的能力已经有一段时间了,但它只是在 Swift 3 的 API 中才真正被接受。
就您何时使用此change 参数而言,如果您要观察多个对象的属性,并且如果这些对象需要在KVO 上进行不同处理,则使用它,例如:
foo.addObserver(self, forKeyPath: #keyPath(Foo.bar), options: [.new, .old], context: &observerContext)
baz.addObserver(self, forKeyPath: #keyPath(Foo.qux), options: [.new, .old], 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
}
if (object as? Foo) == foo {
// handle `foo` related notifications here
}
if (object as? Baz) == baz {
// handle `baz` related notifications here
}
}
顺便说一句,我通常建议使用context,例如,使用private var:
private var observerContext = 0
然后使用该上下文添加观察者:
foo.addObserver(self, forKeyPath: #keyPath(Foo.bar), options: [.new, .old], context: &observerContext)
然后让我的observeValue 确保它是它的context,而不是它的超类建立的:
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
}
if let newValue = change?[.newKey] as? Int, let oldValue = change?[.oldKey] as? Int, newValue != oldValue {
edit()
}
}