現在RAC大行其道,對其講解的博客也多不勝數,稍微有點經驗的估計也已經對這個爽到不要不要的框架運用自如了,真正沉下來研究其實現原理的估計也不在少數,這裏僅僅是記錄一下本身的分析理解,更是在寫這篇博客的過程當中深化本身對RAC的認知,可能就是想到哪寫到哪,各位朋友能從其中學到東西是最好了,要是感受沒什麼乾貨也別對小弟拍磚啊!數組
以下圖:框架
完成一個信號的生命週期分爲四步:異步
下面每一步咱們細細道來:學習
由上面的 信號類使用圖
可知,建立信號類方法中傳入了一個返回值是RACDisposable
類型,參數是遵照 RACSubscriber
協議的吧,名爲 didSubscribe
的block,具體實現以下:設計
由上圖可知,內部建立的是一個 RACDynamicSignal
類型的信號,並將 didSubscribe
傳入,內部實現以下:3d
這裏就是重點了,首先先建立了一個 RACDynamicSignal
類型的信號,而後將傳入的名爲 didSubscribe
的block保存在建立的信號的 didSubscribe
屬性中,此時僅僅是保存並未觸發。code
一句話總結:建立信號本質就是建立了一個 RACDynamicSignal
類型的信號,並將傳入的代碼塊保存起來,留待之後調用。cdn
由上面的 信號類使用圖
可知,有三種訂閱信號的方式,分別是訂閱 Next
、Error
、 completed
,內部實現以下:對象
首先咱們來看第一步,就是建立一個訂閱者,並傳入相應的block,建立實現以下:blog
很明顯,建立訂閱者的實質是,建立一個訂閱者,並保存相應的block,好比 net
、error
、或者complete
,此時僅僅是保存並未觸發!
由上面兩圖可知,三種訂閱方式的流程模式是一致的,僅僅是保存的block不一樣而已,咱們分析一種便可,so 接下來就以 subscribeNext
爲例來逐步分析。
接下來,咱們看看執行訂閱命令這塊的實現,以下:
這裏,首先咱們要知道此處代碼實現是在 RACDynamicSignal
裏,圖中的 didSubscribe
就是第一步建立信號中保存的 didSubscribe
block。
由上圖可知第一步建立信號中保存的 didSubscribe
代碼塊在這裏執行,並傳入了剛剛生成的訂閱者(此處的訂閱者中保存裏 Next
block代碼塊)。
額外的,這裏生成了一個 RACCompoundDisposable
類型的disposable,用來管理整個訂閱結束及資源的清理,並以傳入的訂閱者、及當前信號、剛建立的disposable 生成一個 RACPassthroughSubscriber
類型的訂閱者,此訂閱者僅僅是將傳入的三個對象總體包裝了一下而已,實質起做用的仍是在剛纔建立的訂閱者,因此,其包含的 next
代碼塊,依然直接調用便可。
以後,將執行 didSubscribe
代碼塊返回的 innerDisposable 傳入剛剛生成disposable、並將執行此代碼塊的信號的 schedulingDisposable 也保存到RACCompoundDisposable
類型的disposable中,而後統一管理整個訂閱結束及資源的清理。
其中的innerDisposable 就是 上面的 信號類使用圖
中表示的第四步,其會在信號結束訂閱的時候被調用,作一些清理資源的工做。
而 schedulingDisposable 的話,實際上就是執行代碼塊放到相應的調度器中,假如沒有設置的話,即爲 backgroundScheduler
,並異步執行 didSubscribe
代碼塊,假如設置的話就會返回nil,並直接執行代碼塊,此處嵌套就有點深了,隨後講解取消訂閱的時候再細說。
此處用到的RACCompoundDisposable
類型的disposable有點相似於可變數組 NSMutableArray 。而當 RACCompoundDisposable
對象被 disposed 時,它會調用其所包含的全部 disposable 對象的 -dispose 方法。因此能夠作統一管理。
一句話總結:訂閱信號本質就是建立了一個 RACPassthroughSubscriber
類型的訂閱者,並將傳入的代碼塊保存起來,留待之後調用,同時調用了第一步建立信號中保存的代碼塊,並傳入建立的訂閱者。
由上面的 信號類使用圖
可知,發送一樣對應着三種方式,處理以下:
這裏有三點須要知道
第一,發送信號就是執行相應block,此處執行的就是第二步中保存的相應的block。
第二,對於 sendError
和 sendCompleted
都是先取消訂閱,再執行相應的代碼塊,而 sendNext
並未使訂閱結束,這樣的話,對以後討論的各類組合方法中必須寫上 sendCompleted
來結束訂閱的作法就好理解了。
第三,咱們也看到三種方法中,假如信號沒有相應的block代碼塊保存,即沒有通過第二步去訂閱保存代碼塊,就算是發送了信號也不會執行,此時也就是冷熱信號的區別,固然用 RACSubject
來解釋更容易理解。
一句話總結:發送信號就是執行訂閱信號時對應的block。
通過上面三步,咱們也瞭解到了想要結束訂閱只要將相應生成的disposable執行dispose
便可,那到底爲何呢?如今來說一講:
第二步訂閱信號中有講到,執行訂閱的代碼塊其實是放到相應的調度器中去執行的,以下圖:
如上圖,是否是頗有疑問,假如currentScheduler
不爲nil的話,那豈不是沒有dispose
啥事,代碼塊就直接執行了?我當時也納悶,不過框架中敘述以下(此處感謝雷神指導,麼麼噠!):
此時:代碼塊就不會放到調度器裏去執行,而是直接執行,此時disposable的dispose
方法就沒啥卵用了。
假如currentScheduler
爲nil的話,會默認一個調度器去管理代碼塊,具體實現以下:
如圖所示,可知在代碼塊未被調度的以前,生成的disposable被dispose
的話,代碼塊就不會被執行。
一句話總結:取消訂閱就是把訂閱信號得到的disposable 進行dispose便可在調度器調度該部分代碼以前禁止調用。
RACSignal的原理搞清了,接下來就比較好理解了,畢竟信號類都是繼承與RACSignal,做用不一樣也僅僅是部分實現的方式不一樣而已。
以下圖:
由上圖可知,建立RACSubject對象的時候同時建立了相應的一個disposable和一個訂閱者數組,沒有作其餘事情。
一句話總結:建立信號就是額外實例化了一個訂閱者數組,沒有作其餘事情。
大體流程與RACSignal是一致的,不一樣點在下面:
RACSinal訂閱的時候直接執行了訂閱命令,執行的是建立時保存的代碼塊,但RACSubject的作法是將訂閱者保存到初始化時生成的那個訂閱者數組內,此時每一個訂閱者都包含其對應的代碼塊(好比:next、error、complete)。
由此能夠知道,RACSubject是能夠屢次訂閱的,屢次訂閱就是把相應包含代碼塊的訂閱者放入訂閱者數組內。
另外,此處對於取消訂閱作了一些清理工做,將保存的訂閱者都移除。
一句話總結:訂閱信號就是將代碼塊保存到訂閱者中,並將訂閱者添加到信號的訂閱者數組中。
大體流程與RACSignal是一致的,不一樣點在下面:
由上圖可知,RACSubject的發送信號就是遍歷本身的訂閱者數組,而後分別發送信號。
這也是對月RACsubjec爲何假若有多個訂閱者,發送一個信號全部的訂閱者都收到的緣由。
一句話總結:發送信號就是遍歷本身的訂閱者數組,而後分別發送信號,並執行該訂閱者保存的代碼塊。
仍是那句話,信號類都是繼承與RACSignal,做用不一樣也僅僅是部分實現的方式不一樣而已。
如不一樣的訂閱信號實現:
不一樣的發送信號實現:
授人以魚不如授人以漁,知道了這種分析流程方法,其餘相應的分析相似,朋友們能夠本身試試分析一下,這樣你就會發現其實並非那麼難!
這個就比較複雜了,由於他是基於信號的一個對象,裏面綁定了不少相關的信號,這裏只是講一講他的執行原理,及經常使用的一些方法的分析。
先上圖以下:
由上面 RACCommand流程圖
可知建立的時候傳入一個返回值爲RACSignal類型,參數爲id類型的名爲input的一個block,具體實現以下:
由上圖可知,command的建立實質是額外建立了一個名爲 _activeExecutionSignals
的信號數組,並把傳入的block保存爲 signalBlock
屬性。
初始化的時候實際上還有不少其餘綁定的代碼,就不一一細說了,用到的下面會講一下。
一句話總結:建立命令就是建立了一個RACCommand類的對象,並將傳入的block保存爲 signalBlock
屬性,而後初始化了一個信號數組,留待之後接收命令信號。
由上面 RACCommand流程圖
可知訂閱的信號通過了兩步方法,第一步中executionSignals爲建立命令時綁定的信號以下:
由上圖可知,executionSignals實際上就是最新的第一步建立命令中建立的activeExecutionSignals信號數組。
第二步中的switchToLatest實質上取信號數組的最新的信號。
而後就訂閱信號,月RACSignal的訂閱流程同樣,再也不贅述。
一句話總結:訂閱命令實質上就是訂閱命令保存的信號數組中的最新的信號(目前代碼的訂閱)
與executionSignals相似,在建立命令時也建立了一個信號來監聽當前命令是否正在執行。
如上圖所示,executing信號其實是在檢測是否有活躍信號,replayLast能確保是最新的值。
一句話總結:判斷是否執行命令就是檢測是否有活躍信號
執行命令的話就是傳入你所須要傳入的對象便可,以下圖
由上圖,咱們能夠知道,執行命令先執行了第一步保存的signalBlock並傳入接收到的參數,而後把獲得的signal加入到第一步建立的 _activeExecutionSignals
信號數組中,留待第二步訂閱使用。
一句話總結:執行命令就是將傳入的對象傳入signalBlock生成signal,並將signal添加到_activeExecutionSignals
信號數組中。
在實際開發中咱們並不須要直接調用bind方法,但咱們所使用API都是以bind方法爲基礎的,想要了解操做方法的實現原理,bind咱們不得不深究一下。
先上 bind
流程圖以下:
由上圖可知,bind
操做方法的流程以下:
由此可知,與正常信號綁定訂閱流程不一樣之處在於,多了一個綁定過程,而且最終訂閱的是綁定生成的綁定信號。接下來咱們一個一個理一下思路:
一句話總結:建立了一個 RACDynamicSignal
類型的信號,並將傳入的代碼塊保存起來,留待之後調用。
由 bind操做流程圖
可知,bind
方法傳入了一個 RACStream * (^RACStreamBindBlock)(id value, BOOL *stop)
的block代碼塊,其具體實現以下(只截取核心的代碼):
由上圖可知,bind操做實際上直接返回了一個綁定信號信號,並將 didSubscriber
代碼塊 傳入,保存到返回的信號內。
額外的,綁定信號的 didSubscriber
代碼塊 中作了兩件事
第一,將bind方法傳入的block保存爲bindinfBlock,留待之後用,而且初始化了一個信號數組。
第二,在代碼塊內部訂閱了源信號,並作了一些處理,以下:
由上面可知,訂閱源信號傳入的block,被保存在相應訂閱者中,留待源信號發送信號觸發,內部實現再也不贅述。
一句話總結:bind操做其實是直接生成綁定信號並返回,而且在生成綁定信號傳入的didSubscriber
block代碼塊中,保存了bind傳入的block,初始化了信號數組,而且訂閱了源信號,針對源信號發送信號的流程作了一些處理。(此時未執行,訂閱才執行)
訂閱綁定信號就是實現了第二步中的 didSubscriber
代碼塊,就是說,第二步僅僅是保存,在訂閱它的時候纔會實現。
一句話總結:訂閱綁定信號就是保存了nextBlock
,而且建立訂閱者,實現信號的didSubscriber
block代碼塊。
由於bind比較複雜,因此在此能夠將其串起來
一、發送信號觸發綁定信號 didSubscriber
代碼塊中訂閱源信號時傳入的NextBlock
代碼塊,以下:
二、是執行第三步中訂閱綁定信號保存的從bind傳入的額block代碼塊,並傳入由訂閱時的傳參生成的新的傳參,生成returnSignal,具體實現以下:
三、returnSignal內部實現以下
由代碼可知,returnSignal其實是保存了傳入的新值,此時就是真正意義上的將源輸入作了改變,最後實際輸出的就是新保存的值。
四、接下來就是將生成的returnSignal作 addSignal
操做以下:
由上圖代碼可知,內部實現是直接訂閱了傳入的returnSignal。
五、以前咱們也說過,不一樣信號對應不一樣的訂閱代碼,因此咱們來看看returnSignal的訂閱時的代碼,以下:
由上圖代碼可知,returnSignal在訂閱的時候就同時作了發送信號的任務,即直接就會走訂閱保存的代碼塊,以下:
此處觸發的是綁定信號的訂閱者發送信號,就是執行綁定信號的 nextBlock
,即:
此時就有了運行結果:
ok,bind操做方法就到這了,我已經用盡了個人洪荒之力,麼麼噠!
一句話總結:bind操做方法實質上就是生成新的綁定信號,利用returnSignal做爲中間信號來改變源數據生成新的數據並執行新綁定信號的nextBlock代碼塊!
全部操做方法都是以bind方法爲核心的,這裏分析一下映射方法便可,不在贅述其餘方法,原理都是同樣的,就是看怎麼組合,怎麼去處理相關邏輯達到不一樣操做方法達成的效果!
首先,看一下map的操做流程圖,以下:
很顯然,與 bind
的區別就是生成綁定信號的過程不一樣,具體實現以下:
由上圖可知,從外面傳入了一個參數爲value,返回值爲id類型的block代碼塊,時機返回的是 flattenMap
,操做方法生成的信號,具體實現以下:
看,就是這裏,在 map
方法中,返回的是 flattenMap
方法生成的信號,而 flattenMap
內部返回的是 bind
方法返回的信號,又回到了第一部分講解的 bind
實現原理
上圖中的 stream
,就是returnSignal,即 flattenMap
方法傳入的 block(value),以下圖:
具體生成ReturnSignal的方法,還得往回看 map
方法以下:
由上圖可知,傳入的value已經被 map
保存的方法所映射(即改變),以下:
一句話總結:map
映射方法的實質就是 bind
方法的深度封裝,實際的信號流的流程仍是以 bind
爲核心,只是巧妙的作了一些邏輯處理而已。
對於操做方法,核心就是 bind
,各類做用的不一樣就是各類 signal
與 bind
方法的結合,外加一些巧妙的邏輯處理,明白了信號的原理、綁定的原理,任何其餘的組合或者運用都是萬變不離其宗的,寫更多也不如本身去研究一下!麼麼噠!
因此,操做方法就寫到這裏!
額,這樣就差很少了吧,弄明白了本篇所介紹的幾個經常使用的最基本的原理,其實已經對RAC瞭解的七七八八了,固然RAC還有不少值得去學習的思想,好比RAC宏的使用,各類UI操做的處理,他們其中的設計與原理也都很巧妙,網上也有不少博客介紹了,這裏就暫時不深刻探討了,畢竟寫到這裏篇幅就已經夠長了,你們有興趣能夠本身去看看,之後有時間了我在整理一下!
本文由做者 王隆帥 編寫,轉載請保留版權網址,感謝您的理解與分享,讓生活變的更美好!