RXSwift原始碼淺析(一)

dearmiku發表於2017-12-17

簡述

最近老大給了個新專案,我打算用Swift寫.原來OC用的RAC,換到Swift自然框架也想試試新的,就用了RXSwift,對於這兩個框架,我都是會用,但不解其中的原理,正好最近需求沒下來,就研究了研究RXSwif,把自己的收穫分享一下,文中要有不準確的地方還望大家多多指正~

關於RXSwift是什麼和怎麼用我就不廢話了,網上資源很多,本文先從Observable實現原理入手,旨在以小見大,後面的Single什麼的自然舉一反三~

使用Demo

下面是一段簡單使用Observable的程式碼

        let numbers: Observable<Int> = Observable.create { observer -> Disposable in
            observer.onNext(0)
            observer.onNext(1)
            observer.onCompleted()
            
            return Disposables.create {
            }
        }

        numbers.subscribe{
            print($0)
        }
複製程式碼

demo實現的效果其實就是 將上一段閉包中輸入的 產生的事件(0,1,Completed),在下一段閉包中提取出來. 這樣就將 事件的產生 和 事件的處理 分開. 本文也就是分析這個效果怎麼實現的

主要類

AnonymousObservable

匿名觀察者,儲存產生事件的閉包 和啟用處理事件閉包的入口

AnyObserver

任意觀察者,用於儲存事件 和 輸出事件

AnonymousObserver

匿名觀察者,用於儲存 處理事件的閉包

AnonymousObservableSink

將可觀察者 和 觀察者 連結,實現事件的傳遞

ObserverType,ObservableType..協議

協議,將上面所有內容都包裹起來,將它們加以限制,便於有效的溝通~

Event

事件本身,是列舉,有 Error,Complete,Element(元素)

實現過程

儲存

首先要說的是 ObserverType 定義的一些內容

associatedtype E

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

E:為本次事件流中定義一個確定的型別,保證 產生的和處理的元素型別相同,否則無法傳遞

create方法

Observable<Int>.create { observer -> Disposable in ....} 對於Observable,它是一個抽象類,我們在實際使用中並不能使用它,在協議中有預設的實現

extension ObservableType {
    public static func create(_ subscribe: @escaping (AnyObserver<E>) -> Disposable) -> Observable<E> {
        return AnonymousObservable(subscribe)
    }
}
複製程式碼

所以此處建立的是 AnonymousObservable 物件,我先稱其為A1,A1將事件產生的閉包持有, 閉包中產生的事件 輸入到AnyObserver結構體中.閉包我們成為A2 這樣 儲存部分就好了~~

啟用

啟用 我們通過呼叫A1的訂閱方法subscribe(也是協議中限定的方法),接下來看方法中的實現~ 因為Observable是抽象類,所以這裡也是協議預設的實現

    public func subscribe(_ on: @escaping (Event<E>) -> Void)
        -> Disposable {
            let observer = AnonymousObserver { e in
                on(e)       
            }

            return self.asObservable().subscribe(observer)
    }
複製程式碼

在這裡就分兩步了,一是觀察者的實現,而是事件的傳遞

觀察者

在這裡很簡單,也就是建立AnonymousObserver匿名觀察者物件B1,B1將事件處理閉包持有,閉包我們成為B2

傳遞

首先是asObservable()方法,因為 B1間接繼承自Observable,所以也就是return self,應該是在處理其他型別的可觀察物用到,在後續 如果碰到我會補充~

然後就是對A1的 另一個訂閱方法(過載),將B1作為引數傳入 細枝末節先不說,先把握主幹~

    override func subscribe<O : ObserverType>(_ observer: O) -> Disposable where O.E == Element {
        
        if !CurrentThreadScheduler.isScheduleRequired {
            //第一步
            let disposer = SinkDisposer()
            //第二步
            let sinkAndSubscription = run(observer, cancel: disposer)
            //第三步
            disposer.setSinkAndSubscription(sink: sinkAndSubscription.sink, subscription: sinkAndSubscription.subscription)
            return disposer
        }
        //else先不說~
        else {
            return CurrentThreadScheduler.instance.schedule(()) { _ in
                let disposer = SinkDisposer()
                let sinkAndSubscription = self.run(observer, cancel: disposer)
                disposer.setSinkAndSubscription(sink: sinkAndSubscription.sink, subscription: sinkAndSubscription.subscription)

                return disposer
            }
        }
    }
複製程式碼

第一步

SinkDisposer物件是關於 傳遞結束後,處理資源回收的物件,叫它C1,用來處理 A1create閉包返回的disposer閉包的~

第二步

呼叫了run方法,將B1物件傳入

    override func run<O : ObserverType>(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == Element {
        //2.1  
        let sink = AnonymousObservableSink(observer: observer, cancel: cancel)
        //2.2
        let subscription = sink.run(self)
        //2.3
        return (sink: sink, subscription: subscription)
    }
複製程式碼

2.1步

建立AnonymousObservableSink物件,我稱它D1,它也是將B1物件和C1物件持有

2.2步

呼叫D1物件的run方法,將A1自身傳入

 func run(_ parent: Parent) -> Disposable {
        return parent._subscribeHandler(AnyObserver(self))
    }
複製程式碼

在該方法中,就是將A1物件的A2閉包 呼叫,將D1物件化為AnyObserver結構體作為A2引數傳入~

然後我們看 D1物件 若何轉換的

    //結構體方法
    public init<O : ObserverType>(_ observer: O) where O.E == Element {
        self.observer = observer.on
    }
複製程式碼

在這裡結構體 將 D1持有的B1物件的on方法 作為屬性持有~,將結構體成為E1

再來看E1onNext....方法

extension ObserverType {
    //YSD
    /// Convenience method equivalent to `on(.next(element: E))`
    ///
    /// - parameter element: Next element to send to observer(s)
    public func onNext(_ element: E) {
        on(.next(element))
    }
    
    /// Convenience method equivalent to `on(.completed)`
    public func onCompleted() {
        on(.completed)
    }
    
    /// Convenience method equivalent to `on(.error(Swift.Error))`
    /// - parameter error: Swift.Error to send to observer(s)
    public func onError(_ error: Swift.Error) {
        on(.error(error))
    }
}
複製程式碼

對應的其實是呼叫 B1on方法~~

    func on(_ event: Event<E>) {
        switch event {                      
        case .next:
            if _isStopped == 0 {            
                onCore(event)
            }
        case .error, .completed:

            if AtomicCompareAndSwap(0, 1, &_isStopped) {
                onCore(event)
            }
        }
    }
複製程式碼

對應的B1onCore方法

    override func onCore(_ event: Event<Element>) {
        return _eventHandler(event)         
    }
複製程式碼

也就是將 E1A2接收的事件 傳入B2中,最終實現內容的傳遞~~ 然後再將A1中釋放資源的閉包返回~

2.3

D1和disposable閉包 作為元組返回~

第三步

C1接收元組引數,呼叫setSinkAndSubscription方法~,然後將SinkDisposer物件返回,讓使用者選擇是否釋放~

圖示

文字太抽象,畫個圖吧~ 畫的有點醜(๑•ᴗ•๑)~

RXSwift原始碼淺析(一)

可以看到 A1 在這個過程中只持有了A2, 不會導致記憶體洩露~ 當然如果你dispose 使用不當 肯定有洩漏的~ 親測(๑•ᴗ•๑)~

細枝末節

1

訂閱2中的if !CurrentThreadScheduler.isScheduleRequired

內容是這樣的~

    public static fileprivate(set) var isScheduleRequired: Bool {
        get {     
            //獲取該指示值
            return pthread_getspecific(CurrentThreadScheduler.isScheduleRequiredKey) == nil
        }
        set(isScheduleRequired) {
            
            // 成功返回0            true設定no no設定為 true
            if pthread_setspecific(CurrentThreadScheduler.isScheduleRequiredKey, isScheduleRequired ? nil : scheduleInProgressSentinel) != 0 {
                rxFatalError("pthread_setspecific failed")
            }
        }
    }

    private static var isScheduleRequiredKey: pthread_key_t = { () -> pthread_key_t in
        //YSD
        //https://onevcat.com/2015/01/swift-pointer/
        //可變指標 pthread_key_t型別 分配空間
        let key = UnsafeMutablePointer<pthread_key_t>.allocate(capacity: 1)
        defer {
            key.deallocate(capacity: 1)
        }
        
        //建立執行緒安全的變數
        guard pthread_key_create(key, nil) == 0 else {
            rxFatalError("isScheduleRequired key creation failed")
        }

        return key.pointee
    }()
複製程式碼

這裡應該是為了保護,RXSwift在多執行緒操作下的資料安全~ 在本次事件流中只使用了get方法,並沒使用set~,所以具體效果我不清楚~,以後碰到了 我在補充上吧~

SinkDisposer

就是釋放資源部分~

    fileprivate enum DisposeState: UInt32 {     
        case disposed = 1
        case sinkAndSubscriptionSet = 2
    }

    // Jeej, swift API consistency rules    
    fileprivate enum DisposeStateInt32: Int32 {
        case disposed = 1
        case sinkAndSubscriptionSet = 2
    }
    
    private var _state: AtomicInt = 0
    private var _sink: Disposable? = nil
    private var _subscription: Disposable? = nil
    
    
 func setSinkAndSubscription(sink: Disposable, subscription: Disposable) {
        _sink = sink
        _subscription = subscription

        let previousState = AtomicOr(DisposeState.sinkAndSubscriptionSet.rawValue, &_state)
        if (previousState & DisposeStateInt32.sinkAndSubscriptionSet.rawValue) != 0 {
            rxFatalError("Sink and subscription were already set")
        }

        if (previousState & DisposeStateInt32.disposed.rawValue) != 0 {
            sink.dispose()
            subscription.dispose()
            _sink = nil
            _subscription = nil
        }
    }
    
    func dispose() {
        let previousState = AtomicOr(DisposeState.disposed.rawValue, &_state)

        if (previousState & DisposeStateInt32.disposed.rawValue) != 0 {
            return
        }

        if (previousState & DisposeStateInt32.sinkAndSubscriptionSet.rawValue) != 0 {
            guard let sink = _sink else {
                rxFatalError("Sink not set")
            }
            guard let subscription = _subscription else {
                rxFatalError("Subscription not set")
            }

            sink.dispose()
            subscription.dispose()

            _sink = nil
            _subscription = nil
        }
    }
複製程式碼

從輸出崩潰提示哪裡就可以得知~ 這裡是為了防止dispose的多次呼叫~ 因為在整個事件流中,dipose閉包 可能是 產生Complete,Error或者使用者手動呼叫的~

AtomicOr方法其實呼叫的是OSAtomicOr32OrigBarrier(A,&B) 該函式會將兩個變數 執行緒安全的 按位或運算返回結果, 併為後者賦值=前者~ B=A

未呼叫dipose時 邏輯與運算 state = 2 previousState = 0 兩個條件都不成立~ 所以此時是使用者要手動dispose

之前呼叫過 也就是發生complete 或 Error(在上面的程式碼中也有保證,兩者只發生一起~),則 state = 1當呼叫setSinkAndSubscription方法時 邏輯與運算 state = 2 previousState = 1 則第一個條件不成立 第二個成立~ 釋放資源

當多次Complete時,則只會dipose一次~

當在外界多次呼叫時 則state = 2 previousState = 1 則第一個條件成立 崩潰~

當然這裡實現這種效果的方案有很多種~ RSSwift的方案比較有逼格吧~

總結

看完這些原始碼,我的感覺是RXSwift對 設計模式 貫徹的很徹底~ 在時間富裕的情況下自己寫的專案要向他靠攏,增強專案的延展性,這樣專案經理讓加啥也不會太頭疼了~~

相關文章