RXSwift原始碼淺析(二)

dearmiku發表於2019-03-04

簡述

本文是對上一篇文章的補充,加上對KVO實現的簡要分析 和 自己的一些思考,可能會有不夠準確的地方還望多多指正,共同學習~~

框架結構

在上篇文章中是從具體實現的角度分析Observable的使用實現,接下來從一個高一點的角度來分析RXSwift.

效果

無論是Observable,還是KVO,通知,按鈕點選….,在本質上我認為可將這一切看為這樣一個過程~

效果

以通知為例子, 在程式中某個地方發出了事件(例如鍵盤彈出),這就是事件源. 然後這個事件傳遞(系統發出鍵盤彈出的通知). 最後程式的某處響應了這個事件(比如我們監聽到這個通知,然後將控制元件上移). 我認為RXSwift就是為了讓大家更方便的實現這樣的過程~

而RXSwift的結構就大概是這樣的

結構簡圖

1,事件源

事件源

例如create函式~

2,響應

響應

為了簡潔,我並沒有加入資源釋放那部分, 具體的可以參照上篇進行對比的來看, 中介在事件源中接受事件,在響應中輸出事件.

接下啦我再以KVO的實現細節 再來展示一下這個結構~~

KVO實現細節

下面是一段日常使用RXSwift的程式碼~

class Test: NSObject {
    @objc var name:String!
}

class ViewController: UIViewController {
    @objc var obj:Test!
    var disposeBag = DisposeBag()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        obj = Test()
        obj.rx.observe(String.self, #keyPath(Test.name)).subscribe { (eve) in
            print(eve)
        }.disposed(by: self.disposeBag)
    }
}
複製程式碼

事件源

rx

首先第一步是呼叫物件的rx屬性, rx來源於 對NSObject的擴充套件協議~

import class Foundation.NSObject
extension NSObject: ReactiveCompatible { }
複製程式碼

協議實現


public protocol ReactiveCompatible {
    
    associatedtype CompatibleType   

    // 類屬性 和 物件屬性~
    static var rx: Reactive<CompatibleType>.Type { get set }    
    var rx: Reactive<CompatibleType> { get set }               
}

extension ReactiveCompatible {
    public static var rx: Reactive<Self>.Type {
        get {
            return Reactive<Self>.self
        }
        set {
        }
    }
    public var rx: Reactive<Self> {
        get {
            return Reactive(self)
        }
        set {
        }
    }
}
複製程式碼

這裡其實是為了將當前物件封裝為一個**Reactive**結構體

public struct Reactive<Base> {          
    public let base: Base

    public init(_ base: Base) {
        self.base = base
    }
}
複製程式碼

observe

一般我們呼叫的是這個擴充套件方法

extension Reactive where Base: NSObject {

    public func observe<E>(_ type: E.Type, _ keyPath: String, options: KeyValueObservingOptions = [.new, .initial], retainSelf: Bool = true) -> Observable<E?> {
        
        return KVOObservable(object: base, keyPath: keyPath, options: options, retainTarget: retainSelf).asObservable()
    }
}
複製程式碼

observe方法首先建立一個KVOObservable物件~

//YSD--產生KVO的可觀察者
fileprivate final class KVOObservable<Element>: ObservableType
, KVOObservableProtocol {

    typealias E = 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
        }
    }
    
    //訂閱方法~~~
    func subscribe<O : ObserverType>(_ observer: O) -> Disposable where O.E == 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)
    }
}

..............

fileprivate protocol KVOObservableProtocol {
    var target: AnyObject { get }
    var keyPath: String { get }
    var retainTarget: Bool { get }
    var options: KeyValueObservingOptions { get }
}

複製程式碼

KVOObservable物件遵守ObservableType協議,所以可以呼叫asObsevable()方法, KVOObservableProtocol協議限定它持有這些KVO 必須的屬性~,因為在本質上,還是呼叫OC的KVO實現~

動態語言還是爽呀~

訂閱方法

訂閱時也就是上面過載的方法~ 首先還是建立一個觀察者,來持有 事件響應的閉包~

        let observer = KVOObserver(parent: self) { (value) in
            if value as? NSNull != nil {
                observer.on(.next(nil))
                return
            }           
            observer.on(.next(value as? Element))
        }
複製程式碼

然而事件是從哪裡發出的呢~

fileprivate final class KVOObserver
    : _RXKVOObserver
    , Disposable {

    
    typealias Callback = (Any?) -> Void

    var retainSelf: KVOObserver? = nil

    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
    }

//只用呼叫dispose後才會回收,所以大家注意 不要因為偷懶不好好使用disposeBag(๑•ᴗ•๑)
    override func dispose() {
        super.dispose()     
        self.retainSelf = nil
    }

    deinit {
        #if TRACE_RESOURCES
            _ = Resources.decrementTotal()
        #endif
    }
}
複製程式碼

相對應它的父類就是OC實現的~

@interface _RXKVOObserver ()
                    //和weak差不多, 但是weak釋放了為變為nil 它不會 會因為野指標的使用而崩潰
@property (nonatomic, unsafe_unretained) id            target;
@property (nonatomic, strong           ) id            retainedTarget;
@property (nonatomic, copy             ) NSString     *keyPath;
@property (nonatomic, copy             ) void (^callback)(id);

@end

@implementation _RXKVOObserver

-(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;
}

//常規的操作,將監聽到的新值作為block引數返回
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    @synchronized(self) {
        self.callback(change[NSKeyValueChangeNewKey]);
    }
}

-(void)dispose {
    [self.target removeObserver:self forKeyPath:self.keyPath context:nil];
    self.target = nil;
    self.retainedTarget = nil;
}
複製程式碼

將KVO的newValue作為引數傳入 callBack閉包中~

所以RXSwift對於 KVO的實現就比較簡單了,觀察者既是事件源 也是 中介 總的來說是將 普通的KVO寫法 進行封裝,納入自己的體系之下

反思~

學而不思則罔,思而不學則殆. 所以看完大神的原始碼一定要反思~ 不然除了框架用的更熟練 跟沒看一樣~

面向協議

我認為RXSwift就是一個我們學習面向協議程式設計(Protocol-oriented programming)的好例子~, 通過協議 我們可以很好的解決繼承帶來的種種弊端~

1, 可以實現在OC和Swift不允許的 多繼承~
2, 避免基類受制於子類, 實現依賴倒轉, 讓子類受制於協議~

在RXSwift中有一些重要的協議ObservableType,ObserverType,Disposable

ObservableType

public protocol ObservableType : ObservableConvertibleType {

    func subscribe<O: ObserverType>(_ observer: O) -> Disposable where O.E == E
}
複製程式碼

ObserverType

public protocol ObserverType {
    associatedtype E

    func on(_ event: Event<E>)
}

extension ObserverType {

    public func onNext(_ element: E) {
        on(.next(element))
    }

    public func onCompleted() {
        on(.completed)
    }

    public func onError(_ error: Swift.Error) {
        on(.error(error))
    }
}
複製程式碼

Disposable

public protocol Disposable {
    public func dispose()
}

複製程式碼

通過這些協議 將各個角色類的行為加以限制, 而各個協議之間是互相對接的,這樣即使各個類不相同,只要大家遵守相同的協議,就可以在這個體系下暢通無阻~ . 打個簡單的例子就是 彈幕功能

從伺服器發來的需要展示的訊息是各種各樣的(普通彈幕,禮物彈幕,貴族彈幕,管理資訊彈幕~~~~),當其實在顯示的時候,只需要顯示文字和圖片而已~ 這樣讓 訊息都遵守可以獲取文字圖片的協議,這樣不管發過來什麼訊息 都可以正常顯示~
當然使用繼承也可以實現,但是若我們要加新的訊息型別(禮物火箭),這時繼承要改基類,亂改基類很有可能會影響到其他子類導致Bug,而協議只需要擴充套件,或者限定型別的擴充套件~

當然這不是說讓大家不要用繼承,在RXSwift中也是有繼承的~

final class AnonymousObserver<ElementType> : ObserverBase<ElementType> {......}
複製程式碼

所以我個人是這樣認為的,POP這是綱領,不是方案~ 使用的時候要靈活,小範圍,少變動的用繼承,大範圍,多變化的用協議

單一責任原則

在搭架子的時候就將 角色責任 區分好~ 就像最上面的圖示一樣,,避免類兼數值. 這樣無論是對Bug的定位,還是對專案架構的貫徹 都是有好處的~(๑•ᴗ•๑)

最少知道原則

也就是耦合度的問題,我覺得大家都知道寫程式碼要 高內聚,低耦合. 但是怎麼做呢~ 在RXSwift中是這樣做的,也就是POP 各個角色間通過協議溝通(我到目前為止展示出來的方法,基本上都是協議上的方法),而類通過遵守協議對協議內容進行實現. 這樣 耦合的就只有協議,而類只專注對協議的實現

結尾

暫時就反思了這麼多~然後有內容再補充吧,下篇就寫 flatMap的分析~ 如果老大的需求沒下來 應該很快吧~. 文中要有不準確的地方,請多多指正呀(๑•ᴗ•๑)

才發現掘金可以加封面大圖~~~藍瘦~

相關文章