1. 程式人生 > >ReactiveSwift原始碼解析(六) SignalProtocol的take(first)與collect()延展實現

ReactiveSwift原始碼解析(六) SignalProtocol的take(first)與collect()延展實現

上篇部落格我們聊了observe()、map()、filter()延展函式的具體實現方式以及使用方式。我們在之前的部落格中已經聊過,Signal的主要功能是位於SignalProtocol的協議延展中的,而且延展函式是非常的多的。今天部落格中我們繼續來聊SignalProtocol中那些比較核心的延展實現。本篇部落格我們就來聊一下take()函式的使用以及實現方式,並且再聊一下Signal中collect()的相關實現。

一、take(first)

本部分我們就來聊一下take(first)的使用方式以及具體的實現方式。與上篇部落格的套路類似,我們聊完程式碼後,依然會給出take(first)

函式的運作方式。如果你看過上篇部落格的內容的話,那麼take(first)方法是比較好理解的。因為take(first)也是可以鏈式發展的,並且使用時的整體結構與map()filter()方法差不多,只不過後兩者接收的是一個閉包,而take(first)方法接收的是一個值罷了。

1、take(first)方法的使用

下方示例是ReactiveSwift官方給出的take(first)的使用示例,對其解釋如下:

  • 首先還是通過pipe()函式來建立一個signal訊號量,並獲的其內建的observer
  • 然後通過呼叫signal的take(first)方法來建立一個新的訊號量takeSignal
    。在呼叫take(first)時,傳入的引數是3.
  • 接著建立一個觀察者subscriber,並給出該觀察者在收到Value事件的處理閉包。
  • 最後就是呼叫signal內建的observer來發送Value事件了。

從下方程式碼片段的輸出結果我們不難看出,發出的4個Value事件中只有前三個事件會被takeSignal的觀察者收到。而其他的takeSignal的觀察者是收不到的。從這一點我們就能明確的看出take(first)函式錯建立的訊號量的功能。take()的引數如果是N的話,那麼就表示,take()所返回的訊號量只能接受原訊號量所傳送事件的前N個。

  

2、take(first)方法的具體實現

看完take(first)方法的使用方式,接下來我們就來看一下take(first)的具體程式碼實現。下方的SignalProtocol的延展就是take(first)方法的實現,解釋如下:

  • take()方法也會返回一個新的Signal物件,也是可以鏈式發展的,不過在建立Signal物件時添加了一些條件判斷。
  • 首先必須保證count為非負數的值。
  • 然後當count為0時,直接執行新訊號量中observersendCompleted()方法,結束事件的接收。
  • count > 0時,就建立一個take計數變數來記錄接收Value事件的次數,如果take == count時,說明接收事件的次數已達到上限。就呼叫sendCompleted()方法,結束新返回訊號量的觀察者接收訊息。

  

3、執行原理圖

take()函式的執行過程與map()filter()函式差不多,都是可以鏈式發展的。新建立的Signal物件與之前物件間都會有一個橋接觀察者。這幾個函式間的不同就在於這個橋接觀察者在傳送訊息所遵循的條件不同。下方就是該執行原理圖:

二、Collection的使用

接下來我們來看一下接收集合型別的訊號量。Signal的collect()相關的方法會生成一個新的訊號量,而該訊號量可以接受之前訊號量的集合。也就是說訊號量的值是成批發送的。本部分我們就來聊聊訊號量中的集合的使用方式以及具體的程式碼實現方式。下方是collec相關的延展方法的使用示例。

接下來我們就來一一列舉一下集合訊號量的使用方式,在ReactiveSwift中,主要有4中集合訊號量的使用方式,下面將會一一列舉。

1、collect()

下方程式碼片段就是collect()方法的使用示例,解釋如下:

  • 首先呼叫signal物件的collect()方法生成一個新的訊號量collectionSignal
  • 然後給這個集合訊號量collectionSignal關聯一個觀察者subscriber
  • 然後呼叫原來signal的observer來連續的傳送訊號量,訊號量的值分別為1、2、3、4。最後呼叫一下sendCompleted()方法表示訊號量傳送完畢。
  • 最後我們可以看到,collectionSignal的觀察者會以集合的形式接收到了上述傳送的訊號量的值,如下所示。

  

2、collect(count)

collect(count)該方法比上述方法多了一個count引數,該引數表示集合可容納元素的最大值,當接收訊息的個數為count時,那麼就將該集合進行輸出,並開始下一個集合的積累。下方就是collect(count)的使用方式。

從下方程式碼片段的輸出結果中我們可以看到,輸出結果是兩個集合,因為集合元素個數的最大值我們設定的為3。所以每個集合最多是3個值。 

  

3、collect(_ predicate: @escaping (_ values: [Value]) -> Bool)

該方法接收的是一個條件閉包引數,也就是說predictcate是一個返回Bool型別的條件閉包,閉包中的內容由呼叫者來提供。該方法可以根據該條件閉包的結果來判斷是否將已經收到的訊號量的值進行打包傳送。下方打包傳送的條件是集合中的值的和恰好等於8時才會傳送。

  

4、collect(_ predicate: @escaping (_ values: [Value], _ value: Value) -> Bool)

該函式與上述還是類似,也是接收一個條件閉包,只不過給條件閉包的引數除了當前集合內的元素陣列外,還有一個當前接收但尚未入集合的訊號量的值。我們可以根據這兩個引數做一些判斷,然後在決定是否進行結合訊號量的傳送。

下方程式碼片段的意思是如果當前傳送的值為7,那麼就將之前的集合進行傳送,傳送後並清空,然後將當前為7的值加入到清空後的集合中。下方是呼叫方式以及輸出結果。

  

三、Collection相關訊號量的實現方式

上面我們看完了collect()及其相關方式的使用示例,接下來我我們就來看以前其內部具體的程式碼實現了。本部分就來看一下相關的程式碼實現細節。 

1、CollectState<Value>的實現

首先我們先來看一下CollectState的實現。CollectState就是二次封裝的陣列集合,封裝後更適合我們集合訊號量進行元素的儲存和清空。也就是說,集合訊號量所接收的訊號量的值,是暫存在CollectState這個泛型集合中的。下方就是CollectState<Value>的實現方式。

該程式碼比較簡單就是對陣列進行的二次封裝,其中的append(value)方法就負責往陣列中新增元素,而flush()方法則負責將陣列進行清空。isEmpty計算屬性則用來判斷values陣列是否為空。

  

2、collect(_ predicate: @escaping (_ values: [Value]) -> Bool)方法的實現

接下來我們就來看一下collect(_ predicate: @escaping (_ values: [Value]) -> Bool)方法的實現,該方法中的整體結構與take()以及之前聊的map()、filter()函式相似,都是返回一個新的訊號量,並且將這個新的訊號量的Observer與之前訊號量Bag中的一個Observer物件的事件進行關聯。對下方程式碼片段的解釋如下:

  • 首先在建立新的訊號量的構造器尾隨閉包中建立了一個型別為CollectState<Value>的state,該state集合主要用來暫存原始訊號量傳送過來的值value。而這個新的訊號量的型別為Signal<[Value], Error>型別,也就是說這個新建立的訊號量傳送的值是一個集合型別。
  • 然後在處理之前訊號量傳送事件時,將取出來的值append()到state集合中,然後判斷predicate()閉包條件的值,如果predicate()條件成立,則集合訊號量向其觀察者傳送state集合,傳送後並清除掉state中的元素的值。
  • 當接收到原訊號量的sendCompleted()事件時,集合訊號量先將stata中的元素髮送出去,然後呼叫其本身的sendCompleted()結束訊號量的傳送。

  

3、collect(_ predicate: @escaping (_ values: [Value], _ value: Value) -> Bool)

該方法的實現與上述方法的實現極為相識,只不過是閉包條件所攜帶的引數不同,並且處理條件的方式也不同。由下方程式碼我們不難看出,當條件閉包成立時,集合訊號量進行事件傳送,然後將state集合進行情況,然後在將當前的值新增到已經被清空的state集合中。

也就是說,當條件成立時所傳送的集合中是不包括當前所接收到的來著原始訊號量的值的。具體如下所示:

  

4、collect()和collect(count)的實現

這兩個方式的實現就比較簡單主要是呼叫上述方法,給上述方法的條件閉包傳入不同的值。因實現方法比較簡單,在此就不做過多贅述了。

  

四、collect()工作原理圖

上面聊完collect()的具體工作實現,我們再來看一下collect()的工作原理圖,因為其程式碼實現與map()、filter()、take()等方法類似,所以原理圖也差不多。下方就是collect()的工作原理圖。

從下方的工作原理圖我們清楚的可以看到,原訊號量Signal傳送的是value, collectSignal收到這個value值後,將其暫存到[value]集合中,當predicate()閉包的條件成立時,會呼叫collectSignal的obser的send(value)方法,將集合[value]傳送給collectSignal的觀察者。如下所示:

  

今天的部落格就先到這兒,下篇部落格我們會繼續解析ReactiveSwift框架中的其他內容。