使用RxSwift
後,常常會用到KVO
,可是並無去探究底層是如何實現的,是否和swift
的KVO
實現原理相同,本文將探究RxSwift
裏的KVO
的底層實現原理。編程
先看看Swift裏KVO的基本使用,三步基本操做swift
//添加觀察者
person.addObserver(self, forKeyPath: "name", options: .new, context: nil)
//觀察者響應回調
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
}
//移除觀察者
deinit {
person.removeObserver(self, forKeyPath: "name")
}
複製代碼
RxSwift
裏的KVO
的基本使用,一步到位,簡單粗暴api
// 序列
self.person.rx.observeWeakly(String.self, "name")
.subscribe(onNext: { (change) in
print("observeWeakly訂閱到了KVO:\(String(describing: change))")
})
.disposed(by: disposeBag)
複製代碼
RxSwift
通常用兩種方式使用KVO
閉包
rx.observe
: 執行高效,使用的比較多,是一個KVO
機制的簡單封裝,只能監聽Strong
屬性,不然有崩潰的風險rx.observeWeakly
: 效率較低一些,由於它要處理對象的釋放,防止弱引用。因此它通常使用在weak
屬性上,可是能用rx.observer
的時候,也能使用rx.observeWeakly
observeWeakly
源碼進入,而且繼續跟蹤observeWeaklyKeyPathFor
public func observeWeakly<Element>(_ type: Element.Type, _ keyPath: String, options: KeyValueObservingOptions = [.new, .initial]) -> Observable<Element?> {
return observeWeaklyKeyPathFor(self.base, keyPath: keyPath, options: options)
.map { n in
return n as? Element
}
}
複製代碼
observable
序列對象,點擊observeWeaklyKeyPathFor
看看建立的是個什麼樣的observable
finishWithNilWhenDealloc
是一個容錯機制,對象若是被釋放就直接發送nil完成,沒有觀察的必要,點進去看它的方法內容可以明白private func observeWeaklyKeyPathFor(_ target: NSObject, keyPath: String, options: KeyValueObservingOptions) -> Observable<AnyObject?> {
let components = keyPath.components(separatedBy: ".").filter { $0 != "self" }
let observable = observeWeaklyKeyPathFor(target, keyPathSections: components, options: options)
.finishWithNilWhenDealloc(target)
if !options.isDisjoint(with: .initial) {
return observable
}
else {
return observable
.skip(1)
}
}
複製代碼
weak var weakTarget: AnyObject? = target
把傳入進來的target
對象設置爲用weak
修飾private func observeWeaklyKeyPathFor( _ target: NSObject, keyPathSections: [String], options: KeyValueObservingOptions ) -> Observable<AnyObject?> {
weak var weakTarget: AnyObject? = target
let propertyName = keyPathSections[0]
let remainingPaths = Array(keyPathSections[1..<keyPathSections.count])
let property = class_getProperty(object_getClass(target), propertyName)
if property == nil {
return Observable.error(RxCocoaError.invalidPropertyName(object: target, propertyName: propertyName))
}
let propertyAttributes = property_getAttributes(property!)
// should dealloc hook be in place if week property, or just create strong reference because it doesn't matter
let isWeak = isWeakProperty(propertyAttributes.map(String.init) ?? "")
let propertyObservable = KVOObservable(object: target, keyPath: propertyName, options: options.union(.initial), retainTarget: false) as KVOObservable<AnyObject>
// KVO recursion for value changes
// 後面代碼省略,主要是一個封裝容錯處理
}
複製代碼
isWeak
判斷這個監聽的屬性是不是weak
, 經過判斷屬性值裏有沒有W
來判斷,經過斷點發現監聽的name
屬性是沒有包含W
的private func isWeakProperty(_ properyRuntimeInfo: String) -> Bool {
return properyRuntimeInfo.range(of: ",W,") != nil
}
複製代碼
KVOObservable
,點進去發現它在初始化方法裏保存了須要監聽的屬性和對象等內容,是一個KVO保存者
ObservableType
讓其具備序列的特性,KVOObservableProtocol
讓其擴展幾個協議屬性,是面向協議編程的思路fileprivate final class KVOObservable<Element> : ObservableType , KVOObservableProtocol {
typealias Element = Element?
unowned var target: AnyObject
var strongTarget: AnyObject?
var keyPath: String
var options: KeyValueObservingOptions
var retainTarget: Bool
init(object: AnyObject, keyPath: String, options: KeyValueObservingOptions, retainTarget: Bool) {
self.target = object
self.keyPath = keyPath
self.options = options
self.retainTarget = retainTarget
if retainTarget {
self.strongTarget = object
}
}
//其他代碼先省略
}
複製代碼
subscribe
的流程和咱們以前探索的RX流程基本同樣,很是簡單,直接省略這個流程來到上一步最後的KVOObservable
裏的subscribe
方法KVOObserver
觀察者,並跟隨一個閉包,點進去看看fileprivate final class KVOObservable<Element> : ObservableType , KVOObservableProtocol {
func subscribe<Observer: ObserverType>(_ observer: Observer) -> Disposable where Observer.Element == Element? {
let observer = KVOObserver(parent: self) { value in
if value as? NSNull != nil {
observer.on(.next(nil))
return
}
observer.on(.next(value as? Element))
}
return Disposables.create(with: observer.dispose)
}
}
複製代碼
callback
_RXKVOObserver
和Disposable
fileprivate final class KVOObserver : _RXKVOObserver , Disposable {
typealias Callback = (Any?) -> Void
var retainSelf: KVOObserver?
init(parent: KVOObservableProtocol, callback: @escaping Callback) {
#if TRACE_RESOURCES
_ = Resources.incrementTotal()
#endif
super.init(target: parent.target, retainTarget: parent.retainTarget, keyPath: parent.keyPath, options: parent.options.nsOptions, callback: callback)
self.retainSelf = self
}
override func dispose() {
super.dispose()
self.retainSelf = nil
}
deinit {
#if TRACE_RESOURCES
_ = Resources.decrementTotal()
#endif
}
}
複製代碼
_RXKVOObserver
,發現它居然是一個OC類,說明了RxSwift
的KVO
底層實現利用了OC實現的KVO
的一些相關信息self.target addObserver
最重要的一步操做是這裏進行了觀察者的移交操做,把VC的觀察功能移交給了當前的內部類-(instancetype)initWithTarget:(id)target
retainTarget:(BOOL)retainTarget
keyPath:(NSString*)keyPath
options:(NSKeyValueObservingOptions)options
callback:(void (^)(id))callback {
self = [super init];
if (!self) return nil;
self.target = target;
if (retainTarget) {
self.retainedTarget = target;
}
self.keyPath = keyPath;
self.callback = callback;
// 觀察者移交 - 中間類
[self.target addObserver:self forKeyPath:self.keyPath options:options context:nil];
return self;
}
複製代碼
name
值變化後,回來到當前類裏的下面這個方法callback
方法,而且傳遞了新的鍵值對回去-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
@synchronized(self) {
self.callback(change[NSKeyValueChangeNewKey]);
}
複製代碼
callback
是在初始化時候的閉包,會流轉回去observer.on
發送了響應,value
是剛剛傳遞過來的change[NSKeyValueChangeNewKey]
let observer = KVOObserver(parent: self) { value in
if value as? NSNull != nil {
observer.on(.next(nil))
return
}
observer.on(.next(value as? Element))
}
複製代碼
subsribe
後的閉包,因此這裏可以監聽到這個變化,整個流程清清楚楚.subscribe(onNext: { (change) in
print("observeWeakly訂閱到了KVO:\(String(describing: change))")
})
複製代碼
dispose
沒解析,這裏調用了super
裏的dispose
方法override func dispose() {
super.dispose()
self.retainSelf = nil
}
複製代碼
_RXKVOObserver
裏的銷燬dispose
方法,能夠看到在這裏移除了KVO的觀察者,還置nil了當前對象-(void)dispose {
[self.target removeObserver:self forKeyPath:self.keyPath context:nil];
self.target = nil;
self.retainedTarget = nil;
}
複製代碼
dispose
方法,就會銷燬咱們的KVO觀察者RxSwift
的KVO
底層實現實際上是一箇中介者模式,經過移交觀察者的方式,實現序列的響應和發送效果!併發