RxSwift(十一) KVO基本使用及實現原理探究

前言

使用RxSwift後,常常會用到KVO,可是並無去探究底層是如何實現的,是否和swiftKVO實現原理相同,本文將探究RxSwift裏的KVO的底層實現原理。編程

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
  • 這裏實現了兩個協議,_RXKVOObserverDisposable
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類,說明了RxSwiftKVO底層實現利用了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))")
            })
複製代碼
  • 那麼KVO是怎麼銷燬監聽的呢?
  • 在上面還有一個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觀察者

總結

RxSwiftKVO底層實現實際上是一箇中介者模式,經過移交觀察者的方式,實現序列的響應和發送效果!併發

相關文章
相關標籤/搜索