Swift KVO自定义的属性需要注意

同事跟我说在控制器中不能KVO自定义的属性,我觉得奇怪,就实验了一下(结论当然是可以的,只是写法有问题需要注意,Swift和OC的写法不完全通用)。

demo1:
class ViewController: UIViewController {
    var name = "test"    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        addObserver(self, forKeyPath: "name", options: .new, context: nil)
    }
    
    override func touchesBegan(_ touches: Set, with event: UIEvent?) {
        name = "shine"
    }
    
    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        if keyPath == "name" {
            print("new value = \(change?[NSKeyValueChangeKey.newKey])")
        } 
    }
}

嗯,好像确实没调用观察的方法,没打印。那再试一下自定义一个类,在ViewController中观察类中的属性变化。

demo2:
//ViewController.swift
class ViewController: UIViewController {
    var name = "test"
    var personOne = Person()
      
    override func viewDidLoad() {
        super.viewDidLoad()
        
        addObserver(self, forKeyPath: "name", options: .new, context: nil)
        personOne.addObserver(self, forKeyPath: "age", options: .new, context: nil)
    }
    
    override func touchesBegan(_ touches: Set, with event: UIEvent?) {
        personOne.age = 20
        name = "shine"
    }
    
    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        if keyPath == "name" {
            print("new value = \(change?[NSKeyValueChangeKey.newKey])")
        } else if keyPath == "age" {
            print("new value = \(change?[NSKeyValueChangeKey.newKey])")
        }
    }
}

//Person.swift
 class Person: NSObject {
   var age = 0
}

结果当然是仍然没有打印,观察方法还是没调用。

结论:
  • 一番查找之后发现,观察的类需要加上@objcMembers关键字, 观察的属性需要加上dynamic关键字。
@objcMembers class Person: NSObject {
    dynamic var age = 0
}
  • 用以下的方法观察的类未加@objcMembers关键字,会直接崩溃(Swift 4后继承 NSObject 的 swift class 不再默认全部 bridge 到 OC)。这样会比较清晰的知道写法有问题。而上面的写法只是不调用,不会崩溃。

Fatal error: Could not extract a String from KeyPath Swift.ReferenceWritableKeyPath: file

personOne.observe(\Person.age, options: [.new,.old]) { (person, change) in
            print("new value = \(change.newValue)")
}
  • KVO 是一个纯 OC 的特性,只有NSObject 才能支持 KVO,所以观察的类要继承自NSObject。
  • 观察系统的UITableView的 contentSize,WKWebView的estimatedProgress等自带属性时是不需要注意这些的,系统应该默认实现了。

你可能感兴趣的:(Swift KVO自定义的属性需要注意)