代码之家  ›  专栏  ›  技术社区  ›  Robert

无效值的kvo键路径不起作用

  •  0
  • Robert  · 技术社区  · 6 年前

    我有问题 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]
        }
    }
    

    观察者视图控制器:

    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
        }
    

    谢谢!

    3 回复  |  直到 6 年前
        1
  •  5
  •   rob mayoff    6 年前

    你需要使用 @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)]
    }
    

    你也应该使用 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
    
        2
  •  0
  •   Justin Miller    6 年前

    这样可以解决您的问题。

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

        3
  •  0
  •   Ken Thomases    6 年前

    这个 name surname 属性都需要 @objc dynamic 也一样。因为你的 keyPathsForValuesAffectingFullName 方法,kvo在 fullName 被观察到。 @objc 有必要观察它们。 dynamic 必须使swift在每次设置时都实际调用setter(这是kvo挂接的东西),而不是将其内联并只设置backing instance变量。