上篇博客咱們聊完SignalProducer結構體的基本實現後,咱們接下來就聊一下SignalProducerProtocol延展中的start和lift系列方法。SignalProducer結構體的方法擴展與Signal的擴展相同,都是面向協議的擴展。首先建立了一個SignalProducerProtocol協議,使SignalProducer在延展中遵循SignalProducerProtocol協議。而後咱們再對SignalProducerProtocol進行擴展。這樣一來,SignalProducer結構體就擁有了咱們在SignalProducerProtocol協議中擴展的方法了。這也是咱們以前所說的「面向協議的擴展」。git
今天咱們就來聊一下SignalProducerProtocol協議擴展中的start和lift系列方法。不管是start系列方法仍是lift系列方法,都是在SignalProducer的startWithSignal(setup)核心方法的基礎上構建的。而關於startWithSignal(setup)方法的具體實現,上篇博客給出了相應的介紹並給出了該核心方法的使用方式,在此就不作過多贅述了。github
而在SignalProducerProtocol協議擴展中的方法,基本上全是對b方法的封裝,只不過使用場景和功能更爲專注,用法更爲方便。接下來咱們就來看一下SignalProducerProtocol協議擴展的start和lift系列方法。算法
1、Start系列方法閉包
在SignalProducerProtocol協議擴展中的Start系列方法的主要做用是往SignalProducer中的signal的Bag中添加觀察者的,這一點與Signal的observer()系列方法相似。下方就是start系列的部分方法,在下列的方法中,核心的是start(observer)方法。該方法的參數是一個觀察者的對象,start(observer)方法就負責將該觀察者添加到SignalProducer中的signal的Bag。而下方一系列的start方法都是調用的start(observer)方法。框架
由於Start系列方法極爲類似,在此就不一一進行列舉了,下方是部分start方法,其他省略的與下方代碼的實現原理一致,在此就不作過多贅述了。函數
看完start系列方法的實現,咱們再來看一下start系列方法使用方式。下方是start系列方法的部分使用案例,具體介紹以下:測試
首先經過 SignalProducer的init(value)構造器建立一個producer對象,備用。
而後再建立一個觀察者 subscriber1,並給出Value事件的處理閉包。
而後調用 start(observer)和startWithSignal()方法將subscriber1添加到信號量中。
以後再調用一些列start()方法往signal中添加新的信號量。
下方的控制檯中是該代碼段實例的輸出結果,從輸出結果咱們咱們知道,start()系列方法的主要做用是往signal中添加觀察者的。觀察者添加完畢後,就調用SignalProducer構造函數的尾隨閉包。具體代碼以下所示。spa
2、閉包類型的高級用法3d
在聊Lift以前呢,咱們先來看一下閉包類型使用的示例。由於Lift相關方法的實現較複雜一些,其中涉及閉包方法類型的一些高級用法。接下來咱們從Swift語言的角度來看一下函數類型的高級用法,該用法也是在Lift中使用到的,仍是有必要單獨的拎出來聊一下的。下方是一些具體的示例。orm
一、建立MyClass類
首先咱們建立一個簡單的類MyClass,該類比較簡單,就是一個屬性、一個構造器、一個add方法。add(other)方法是MyClass的主角稍後咱們會用到。由於下方的代碼比較簡單在此就不作過多贅述了。MyClass下方緊跟着的就是MyClass類測試用例,以下所示。
二、建立MyClassProducer類
接下來咱們建立一個MyClassProducer類,在該類中使用到了MyClass類。在MyClassProducer中也有一個add方法,只不過該add方法接收的是一個閉包參數,而返回值是一個MyClass類型。add()的參數類型爲(MyClass) -> (MyClass) -> MyClass。該閉包類型接收一個MyClass類型的參數,而後返回一個(MyClass) -> MyClass類型的閉包。而(MyClass) -> MyClass類型的閉包的參數又是一個MyClass類型,而返回值是MyClass。
在add(closure)方法中直接執行了closure閉包,並將閉包最終返回的MyClass類型的對象進行返回。具體代碼以下所示。
三、MyClassProducer類的很是規用法
接下來咱們就來看一下MyClassProducer類中add(closure)方法直接的使用方式。固然,在正常狀況下,上述寫法尤其繁瑣,而是使用方式也是比較麻煩的。下方咱們就來看一下直接調用add(closure)方法的代碼。
首先咱們建立了一個MyClassProducer類型的對象myProducer1。
而後直接調用myProducer1的add(closure)方法。add()的尾隨閉包的參數是MyClass類型的對象myclass1,其返回值是(MyClass)->MyClass類型的閉包,因此咱們就直接在尾隨閉包塊中返回了一個閉包塊,該返回的閉包塊的類型就是 (MyClass)->MyClass。而後在(MyClass)->MyClass類型的閉包塊中返回了一個MyClass類型的對象,在建立該對象時,使用到了上述兩個閉包的參數myclass1和myclass2。
而後咱們將sum01對象的des屬性進行打印,就是closure閉包兩個參數的和。具體結果以下所示。
四、MyClassProducer類中add方法的常規用法
上一部分中add(closure)方法的使用方式是很是規用法,由於直接使用add(closure)方法顯得晦澀難懂,並且閉包嵌套閉包,閉包返回閉包的形式着實讓人費解。能夠說直接使用沒有什麼好處。接下來咱們就來看一下add(closure)的常規使用方式。
下方代碼片斷是add(closure)反覆的常規使用方式。從下方代碼片斷中咱們能夠直接看出,add(closure)接收的不在是一個閉包,而是MyClass.add(other:)的類型。其實就是把MyClass.add(other:)這個類型所對應的方法體傳給了add(closure)閉包。也就是說add(other:)方法的類型與(MyClass) -> (MyClass) -> MyClass閉包類型是等價的。因此能夠將add(other:)方法的方法體提供給add(closure)做爲參數。換句話說(MyClass) -> (MyClass) -> MyClass類型等價於MyClass.add(MyClass)->MyClass。
這樣作的好處就是可讓數據與算法進行分離,add(closure)參數閉包對應什麼樣的算法那麼add(closure)就執行什麼樣的算法。這一點在SignalProducer類中的Lift系列方法中表現的淋漓盡致。稍後咱們會介紹到。
3、Lift系列的核心方法實現
接下來咱們就來看一下SignalProducer中的Lift系列方法的代碼實現。固然,由於Lift系列方法比較多,下方會給出Lift系列方法中比較核心的內容,而剩下的未講解的則是從這些核心方法中延伸出來的方法。接下來咱們就由易到難,來看一下Lift系列方法的代碼實現。
一、lift<U, F>(transform)代碼實現
該方法算Lift系列中比較獨立並且比較核心的方法了。下方代碼片斷就是該方法的實現。解釋以下:
該方法是一個泛型方法,能夠容納兩個泛型 <U, F>。其方法參數是一個逃逸閉包transform,而這個閉包的參數是一個類型爲 Signal<Value, Error>的信號量,而返回值是類型 Signal<U, F>的信號量,也就是說transform閉包的功能負責將 Signal<Value, Error>類型的信號量通過某些算法轉換成Signal<U, F>類型的信號量。而整個函數的返回值是一個 SignalProducer<U, F>類型的信號量生產者。
方法體中,返回了一個新的SignalProducer對象,在SignalProducer構造器的尾隨閉包中調用了原SignalProducer對象的 startWithSignal()方法。在 startWithSignal()方法的尾隨閉包中將原SignalProducer對象的信號量signal通過transform閉包轉換成一個新的信號量後,將新SignalProducer對象的observer添加到這個轉換後的信號量的Bag中,成爲其觀察者。具體代碼以下所示。
爲了更進一步來了解上述代碼的實現方式以及運行方式,咱們還需結合示例進行分析。下方代碼片斷就是上述方法的使用示例,介紹以下:
首先建立了一個類型爲 SignalProducer<Int, NoError>的對象producer。在該對象的尾隨閉包中,發送了一個 Value事件,該事件的值爲整數8888。
而後經過producer對象的lift方法建立了一個新的對象liftProducer。在lift方法的尾隨閉包中將producer對象內部的 Signal<Int, NoError>類型的signal信號量經過信號量的map方法將其轉換成 Signal<String, NoError>類型的信號量,並返回。有下方代碼以及lift()方法的實現容易知道,由於liftProducer對象中的Observer對象被添加到轉換後的 Signal<String, NoError>類型的信號量中做爲觀察者,因此liftProducer對象的類型是 SignalProducer<String, NoError>。
下方代碼中 lift()方法的尾隨閉包就是上述函數實現中的transform的閉包體。下方的signal參數就是transform在調用時傳入的參數。
接着,咱們有建立了三個類型爲 Observer<String, NoError>類型的觀察者,而後將這些觀察者都添加進行liftProducer對象的信號量中。而後咱們會看到控制檯上打印的觀察消息。
根據lift(transform)的代碼實現以及上述示例的運行結果,咱們給出了下方的原理圖。下方這個簡圖就是上述示例執行的整個過程,一圖勝千言。根據下圖結合上述示例應該是一目瞭然的。在此就不在過多贅述了。
在SignalProducer的延展中,下方的方法全是在上述lift(transform)的基礎上實現起來的,歸根結底使用的仍是Signal中相應的方法。下方這些方法的工做方式以及運行原理和上面這個圖很是類似。只不過是生成中間的信號量的方式不一樣。
下方代碼片斷中每一個方法在使用lift(transform)方法時使用了尾隨閉包的簡寫形式。其中的$0參數就是尾隨閉包的Signal參數,$0信號量經過調用其對應的方法生成的新的信號量就是該尾隨閉包的返回值。具體以下所示。固然下方只是部分使用lift(transform)的方法,其餘的與下方相似,就不作過多贅述了。
二、liftRight<U, F, V, G>(transform)代碼實現
在看liftRight方法的代碼實現以前呢,仍是須要回顧一下本篇博客的第二部分閉包類型高級用法的內容的。由於本篇博客第二部分中的內容以及使用示例有助於理解liftRight方法的使用方式以及運行模式。
下方代碼片斷就是liftRight方法的具體實現,咱們須要注意的是liftRight方法是private類型的,也就是說該方法不對用戶直接暴漏,用戶不能夠直接調用該方法。固然,此刻咱們須要看liftRight方法的代碼實現,要給出相應的使用示例,因此咱們能夠將private改爲public。
從下方代碼實現中,咱們能夠直觀的感覺到liftRight方法的代碼實現是比較複雜的。其複雜就複雜在liftRight的入參和返回值都是比較複雜的。首先咱們來看一下liftRight參數。其參數是一個名爲transform的閉包,該閉包的類型爲(Signal<Value, Error>) -> (Signal<U, F>) -> Signal<V, G>,該閉包類型須要一個Signal<Value, Error>類型的參數,其返回值是一個類型的(Signal<U, F>) -> Signal<V, G>閉包。
從該閉包類型,而後在參考第二部分中的(MyClass)->(MyClass)->MyClass閉包類型,以及該閉包類型與MyClass.add(MyClass)->MyClass方法的對應關係。咱們不難看出(Signal<Value, Error>) -> (Signal<U, F>) -> Signal<V, G>類型的閉包等價於Signal<Value, Error>.method(Signal<U, F>)->Signal<V, G>類型的方法。而在Signal類中有好多符合Signal<Value, Error>.method(Signal<U, F>)->Signal<V, G>類型的方法,如Signal中的combineLatest、withLatest、take(until:)、skip(until:)等方法,也就是說這些方法的方法體均可以做爲transform閉包的閉包體,稍後咱們會進行介紹。
三、liftRight<U, F, V, G>(transform)直接調用
按照老規矩,咱們先給出liftRight方法的使用方式,固然此處是liftRight方法的使用方式是很是規的作法,由於咱們是直接拿過來用的。不過這樣作更有利於咱們理解liftRight的代碼結構和運行方式。
下方代碼片斷就是咱們直接調用liftRight方法的示例,介紹以下:
首先咱們經過常規方法建立了一個類型爲 SignalProducer<String, NoError>的對象producer。
而後根據liftRight方法的代碼實現,建立了兩個類型別名。 LiftRightProducerClosureType類型就是producer對象調用liftRight方法是所返回的閉包類型,ClosureReturnType則是liftRight方法的參數的閉包所返回的閉包類型。
類型定義好後,就該讓producer對象調用liftRight方法了。而下方的 liftRightProducerClosure(SignalProducer)則是該方法返回的閉包常量。在該liftRight的返回閉包中,咱們將producer對象所對應的信號量signal以及 liftRightProducerClosure閉包所接收的SignalProducer對象中的信號量otherSignal,調用了combineLatest方法進行了合併,具體作法以下。
而後又建立了一個strProducer對象,併爲其綁定了一個signal信號量。而後執行 liftRightProducerClosure(strProducer),該閉包會返回一個新的otherProducer對象,緊接着執行otherProducer的startWithValues()方法。而後調用strProducer所綁定信號量的Observer發送值。具體結果以下所示:
針對上述代碼的執行過程,仍是來張圖來的直接。下方這張簡圖就是上述代碼的執行過程。執行過程,與上述代碼的執行步驟是一一對應的。能夠根據代碼的運行步驟後下方的簡圖進行比較。關於下方簡圖,就不作過多贅述了。
四、liftRight<U, F, V, G>(transform)常規使用方式
上面一小節咱們直接調用了liftRight方法,接下來咱們就來看一下liftRight的常規使用方式。所謂的常規使用方式是使用已經實現過的方法的方法體做爲transform的閉包體。上面列舉了一些Signal中的方法類型與transform的閉包類型等價的方法其中就有Signal.combineLatest(Signal)->Signal方法。接下來咱們就使用combineLatest的方法體來替換上述liftRight方法的尾隨閉包。
下方紅框中就是咱們替換的內容,其餘代碼不變。咱們發現替換後,輸出結果與咱們以前一致。下方的這種使用方式纔是liftRight方法正確的使用姿式。
看完上述liftRight的實現以及使用方式,接下來咱們就來看一下SignalProducer內部是如何使用liftRight方法的。下方隨便找了一個liftRight的使用方式,觸類旁通。
4、liftRight方法與liftLeft方法對比
而在Lift系列方法中,使用liftRight()方法的方式就是上述代碼段的方式。稍後咱們會一一介紹。上述這種技巧使用起來仍是比較方便的。出來liftRight方法,還有一個liftLeft()方法。liftLeft()方法的實現方式與liftRight()方法代碼實現即爲類似,只是producer的startWithSignal()方法的調用順序不一樣。接下來咱們就來看看這二者的不一樣之處。
下方代碼片斷就是liftRight以及liftLeft方法的代碼實現。通過對比咱們不難發現二者的主要區別是otherProducer和self的startWithSignal()方法的執行順序不一樣。在liftRight()方法中otherProducer的startWithSignal會先執行完畢,而self的startWithSignal()會後執行完畢。而liftLeft剛好於此相反。
咱們以producer.liftRight()(otherProducer)爲例,這個Right指右邊的otherProducer的startWithSignal()方法率先執行完畢。而producer.liftLeft()(otherProducer)則指左邊的producer的startWithSignal()方法率先執行完畢。
爲了更直觀的感覺上述兩個方法的不一樣之處,特此給出了下方的示例。根據下方示例的輸出結果,liftRight與liftLeft的區別一目瞭然。對下方示例的介紹以下:
首先咱們建立了兩個 SignalProducer的對象,一個發送0,1,2的值,另外一個發送A、B、C的值。
而後讓producer1對象調用liftRight方法, 使用liftRightProducerClosure來暫存返回的閉包,將producer2傳入閉包中。而後調用rightProducer的startWithSignal方法。以相似的步驟調用lifeLeft方法
根據上述代碼片斷的輸出結果,咱們不難看出在producer.liftRight()(otherProducer)中右邊的otherProducer的startWithSignal()方法率先執行完畢。而producer.liftLeft()(otherProducer)則指左邊的producer的startWithSignal()方法率先執行完畢。
下方是liftLeft()方法是使用方式,與liftRight用法是一致的,以下所示:
在SignalProducer的好多方法中都是在lift、liftRight或者liftLeft方法的基礎上實現的特定功能。之後的博客會陸陸續續的介紹到。因篇幅有限,今天的博客就先到這兒,下篇博客咱們會繼續解析ReactiveSwift框架中的其餘內容。
上述代碼github分享地址:https://github.com/lizelu/TipSwiftForRac