同事跟我说在控制器中不能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
等自带属性时是不需要注意这些的,系统应该默认实现了。