首页 > 解决方案 > KVO keyPathsForValuesAffecting 不起作用

问题描述

我的方法有问题keyPathsForValuesAffecting<key>。我想通知观察者fullName何时namesurname发生了变化。但不幸的是,没有通知观察员。

我的代码:

将被观察的类:

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]
    }
}

观察者视图控制器:

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
    }

谢谢!

标签: iosswiftkey-value-observing

解决方案


您需要@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

推荐阅读