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<UITouch>, 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<UITouch>, 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<testTwo.Person, Swift.Int>: 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等自带属性时是不需要注意这些的,系统应该默认实现了。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容