我对keyPathsForValuesAffecting<key>
方法有疑问。当fullName
或name
更改时,我想通知观察者surname
。但是不幸的是观察者没有得到通知。
我的代码:
将要观察的课程:
class DependencyTest: NSObject {
@objc dynamic var fullName: String {
return name + " " + surname
}
@objc var name = ""
@objc var surname = ""
class func keyPathsForValuesAffectingFullName() -> Set<NSObject> {
return ["name" as NSObject, "surname" as NSObject]
}
}
观察者ViewController:
let dep = DependencyTest()
override func viewDidLoad() {
super.viewDidLoad()
addObserver(self, forKeyPath: "dep.fullName", options: .prior, context: nil)
dep.name = "bob" // Im expecting that `observeValue:` method will be fired
dep.surname = "gril"
}
override func observeValue(forKeyPath keyPath: String?,
of object: Any?,
change: [NSKeyValueChangeKey : Any]?,
context: UnsafeMutableRawPointer?) {
print("" + keyPath!) // not called
}
谢谢!
答案 0 :(得分:5)
您需要在@objc
方法上使用keyPathsForValuesAffecting
,以便KVO机械可以使用Objective-C运行时找到它:
@objc class func keyPathsForValuesAffectingFullName() -> Set<NSObject> {
return ["name, "surname"]
}
顺便说一句,您可以改用属性,也可以使用#keyPath
特殊形式来使编译器帮助您捕获错误:
@objc class var keyPathsForValuesAffectingFullName: Set<String> {
return [#keyPath(name), #keyPath(surname)]
}
您还应该按照Ken Thomases的建议在上游属性(dynamic
和name
)上使用surname
。
这是一个完整的测试程序(作为macOS命令行程序):
import Foundation
class DependencyTest: NSObject {
@objc dynamic var fullName: String {
return name + " " + surname
}
@objc dynamic var name = ""
@objc dynamic var surname = ""
@objc class var keyPathsForValuesAffectingFullName: Set<String> {
return [#keyPath(name), #keyPath(surname)]
}
}
class Observer: NSObject {
@objc let dep: DependencyTest
init(dep: DependencyTest) {
self.dep = dep
super.init()
addObserver(self, forKeyPath: #keyPath(Observer.dep.fullName), options: .prior, context: nil)
}
deinit {
removeObserver(self, forKeyPath: #keyPath(Observer.dep.fullName), context: nil)
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
print("kvo: \(keyPath!) \(change?[.notificationIsPriorKey] as? Bool ?? false ? "prior" : "post")")
}
}
let d = DependencyTest()
let o = Observer(dep: d)
d.name = "Robert"
输出:
kvo: dep.fullName prior
kvo: dep.fullName post
Program ended with exit code: 0
答案 1 :(得分:0)
这应该可以解决您的问题。
class ViewController: UIViewController {
@objc let dep = DependencyTest()
override func viewDidLoad() {
super.viewDidLoad()
self.addObserver(self, forKeyPath: #keyPath(dep.fullName), options: .new, context: nil)
self.dep.name = "bob" // Im expecting that `observeValue:` method will be fired
self.dep.surname = "gril"
}
override func viewWillDisappear(_ animated: Bool) {
self.removeObserver(self, forKeyPath: #keyPath(dep.fullName))
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
print("" + keyPath!) // not called
print("Full Name = ", dep.fullName)
}
}
class DependencyTest: NSObject {
@objc dynamic var fullName: String = ""
var name = "" {
didSet {
fullName = name + " " + surname
}
}
var surname = "" {
didSet {
fullName = name + " " + surname
}
}
}
将无法观察作为函数运行的变量,您必须设置一个实际值。还请记住要删除观察者,因为这不是本机的swift函数,它不会取消初始化,并且在您关闭它后可以将VC保留在内存中。
答案 2 :(得分:0)
name
和surname
属性也都必须为@objc dynamic
。由于使用了keyPathsForValuesAffectingFullName
方法,因此KVO会在观察到fullName
时观察到它们。 @objc
是允许被观察到的必要条件。 dynamic
对于使Swift每次设置时实际上调用setter(KVO所钩挂的东西)是必要的,而不是内联它而只是设置背景实例变量。