在Swift中使用KVO,有如下兩種方法:
原OC提供的:
open func addObserver(_ observer: NSObject, forKeyPath keyPath: String, options: NSKeyValueObservingOptions = [], context: UnsafeMutableRawPointer?) @available(iOS 5.0, *) open func removeObserver(_ observer: NSObject, forKeyPath keyPath: String, context: UnsafeMutableRawPointer?) open func removeObserver(_ observer: NSObject, forKeyPath keyPath: String)
使用上面提供的方法實現KVO,需要注意一點,keyPath對應的屬性,必須要是OC的屬性,如果是通過swift定義的屬性,必須在屬性前添加@objc和dynamic修飾才可以,否則KVO將無效果,如下:
class ViewController: UIViewController { // swift定義的屬性,必須使用@objc和dynamic標注,否則kvo將無效果 @objc dynamic var name: String = "" override func viewDidLoad() { super.viewDidLoad() // 添加KVO addObserver(self, forKeyPath: "name", options: [.new], context: nil) // 修改name值 name = "drbox" } override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { print("kvo: name: \(change?[.newKey] as? String ?? "")") } }
swift提供的:
func observe<Value>(_ keyPath: KeyPath<ViewController, Value>, options: NSKeyValueObservingOptions = [], changeHandler: @escaping (ViewController, NSKeyValueObservedChange<Value>) -> Void) -> NSKeyValueObservation
使用上面提供的方法實現KVO,注意事項同上,代碼如下:
class ViewController: UIViewController { // swift定義的屬性,必須使用@objc和dynamic標注,否則kvo將無效果 @objc dynamic var name: String = "" var obs: NSKeyValueObservation? deinit { // 移除觀察者 obs = nil } override func viewDidLoad() { super.viewDidLoad() // 添加kvo obs = observe(\.name, options: [.new], changeHandler: { (_, change) in print("name: \(change.newValue ?? "")") }) name = "drbox" } }
使用過rxswift都知道,它同樣提供了KVO的實現,實現原理實際上就是對OC的KVO的封裝,因此在使用時同樣要注意以上事項:@objc dynamic
class ViewController: UIViewController { @objc dynamic var name: String = "" var disposeBag = DisposeBag() override func viewDidLoad() { super.viewDidLoad() // 方法一: rx.observe(\.name).bind { str in print("name1: \(str)") }.disposed(by: disposeBag) // 方法二: rx.observe(String.self, "name").map({ $0 ?? ""}).bind { str in print("name2: \(str)") }.disposed(by: disposeBag) // 方法三: rx.observeWeakly(String.self, "name").map({ $0 ?? ""}).bind { str in print("weak name: \(str)") }.disposed(by: disposeBag) name = "drbox" } }
其中rx.observe與rx.observeWeakly的區別在於前者返回的Observable會對觀察者target強持有(strong);后者返回的Observable會對target弱持有(weak)
具體的比較如下:
性能比較:
- rx.observe 更加高效,因為它是一個 KVO 機制的簡單封裝。
- rx.observeWeakly 執行效率要低一些,因為它要處理對象的釋放防止弱引用(對象的 dealloc 關系)。
使用場景比較:
- 在可以使用 rx.observe 的地方都可以使用 rx.observeWeakly。
- 使用 rx.observe 時路徑只能包括 strong 屬性,否則就會有系統崩潰的風險。而 rx.observeWeakly 可以用在 weak 屬性上。
當然rx.observe也可以決定其返回的Observable是否強持有target,可以設置參數:retainSelf,默認:true(強持有)
// retainSelf rx.observe(String.self, "name", retainSelf: true).map({ $0 ?? ""}).bind { str in print("name2: \(str)") }.disposed(by: disposeBag)
