KVO keyPathsForValuesAffecting 不起作用

KVO keyPathsForValuesAffecting doesn't work

我对 keyPathsForValuesAffecting<key> 方法有疑问。当 namesurname 发生变化时,我想通知 fullName 的观察者。但不幸的是观察者没有得到通知。

我的代码:

class 将被观察到:

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
    }

谢谢!

这应该可以解决您的问题。

 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 保存到内存中。

namesurname 属性也必须是 @objc dynamic。由于您的 keyPathsForValuesAffectingFullName 方法,KVO 在观察到 fullName 时观察它们。 @objc 是观察它们所必需的。 dynamic 是使 Swift 每次设置时实际调用 setter 所必需的(这是 KVO 挂钩的),而不是内联它并仅设置支持实例变量。

您需要在 keyPathsForValuesAffecting 方法上使用 @objc 以便 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 的建议,在上游属性(namesurname)上使用 dynamic

这是一个完整的测试程序(作为 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