1. 程式人生 > >ReactiveSwift原始碼解析(一) Event與Observer程式碼實現

ReactiveSwift原始碼解析(一) Event與Observer程式碼實現

ReactiveCocoa這個框架是做什麼用的本篇部落格就不做過多贅述了,什麼是“響應式程式設計”也不多聊了,自行Google吧。本篇部落格的主題是解析ReactiveCocoa框架中的核心模組ReactiveSwift中的兩個核心類的實現,也就是對Event和Observer這兩個類進行解析。之所以把這兩個類放在一塊聊,是因為這兩個類比較獨立,可以說是ReactiveSwift中的兩個原子類Event確切的說是一個列舉,其中有幾種事件,而Observer類的物件就是這些事件的傳送者。所以把這兩個類放在一塊是比較合適的。

當然確切的說,本篇部落格是對 ReactiveSwift框架 的部分解析,而ReactiveCocoa這個框架又是在ReactiveSwift框架的基礎上搭建起來的,所以我們先來看一下ReactiveSwift這個框架中的程式碼實現。當然,我們之前發表過ReactiveCocoa的相關博文,如《》,該篇部落格的主題還是ReactiveCocoa框架的應用,而本篇部落格或者說ReactiveCocoa原始碼解析系列部落格是對ReactiveCocoa框架實現的深度解析。當然這種深度解析有一部分是Swift語言層面的,因為ReactiveCocoa框架中有好多Swift語言的高階用法,當然還有一些架構層面的,通過原始碼實現,我們要分析出這樣設計的好處以及優點。

拋去“響應式程式設計”的概念,ReactiveCocoa的本質還是對“觀察者模式”的使用,關於觀察者模式,請參考之前的部落格《》。也可以說ReactiveCocoa是“觀察者模式”應用中比較牛X的一個框架。當然,框架在編碼實現時還用到了其他設計模式,在解析到相關內容時,我們在對其進行概述。

當然,本篇部落格是對ReactiveSwift原始碼的解析,也就是說你可以在你的工程中僅僅的引入 ReactiveSwift框架 ,GitHub地址為:,至於如何將ReactiveSwift引入到的你的工程中,請參考ReactiveSwift下方的README, 當然,本篇部落格是使用的Cocoapods

來實現的版本管理,當然ReactiveSwift也支援Carthage, 如果你是Mac開發的話,還可以使用Swift自帶的包管理器。Swift的包管理器我們在之前聊Swift開發服務端的時候使用到了,不過目前iOS開發中還不能使用Swift自帶的包管理器。相信在不久的將來Swift的包管理器將會支援iOS開發的。閒淡適中,開始我們的主題。

本篇部落格我們將先在Swift語言的層面來聊一些東西,因為在Event和Observer實現時會用到。然後我們再解析一下Event和Observe的實現。之前我們聊過Swift語法層面的東西,不過今天還是要在聊一下的,結合著例項還聊語法最為實用。

一、Swift中的泛型

ReactiveSwift以及ReactiveCocoa中大量的用到了泛型以及關聯型別,所以在聊原始碼之前,我們還是有必要回顧一下Swift中的泛型的使用的。當然,只是簡單的回顧一下,不是今天部落格的重點。首先我們得通過一個例項來看一下泛型的使用。

下方這個程式碼段,就是在協議中使用 associatedtype 關鍵字聲明瞭一個關聯型別,當然這個關聯型別就相當於協議中的泛型了。下方的這個 GenericityClass 類後邊的<>中宣告的就是該類中使用的泛型型別,我們將該泛型命名為 MyCustomType, 當然我們要求該型別必須是遵循 Comparable 協議的型別,所以宣告該泛型的形式為 <MyCustome: Comparable>。宣告完該泛型後,在類中我們就可以想使用普通型別那樣來使用該泛型了。

泛型不僅僅可以在類中使用,也可以在方法中使用,下方的genericityFunc()方法中就使用了泛型,用法就是在方法名的後方緊跟著泛型,如下所示。

接下來我們來看一下上述泛型類的使用方式。下方程式碼首先聲明瞭一個泛型類的例項,在例項化時,給泛型指定了確定的型別 String。我們還可以為相應的的泛型型別使用 typealias 指定別名,然後使用別名來例項化,如下所示。因為程式碼比較簡單,下方測試用例的輸出結果就不往上貼上了。

  

二、Swift中的列舉

因為今天我們要聊的Event就是個列舉,所以我們先來回顧一下Swift中列舉的使用。當然還是依託於例項。下方程式碼中的列舉是在我們之前聊Swift的列舉的主題中拿過來的,並且做了相應的修改。當然在Swift中列舉以及結構體都是可以使用泛型的,接下來我們就來好好看一下Swift中強大而靈活的列舉型別。

下方程式碼片段中我們定義了一個MobileLanguage列舉型別,其中有兩個列舉項。一個是iOS,另一個是Android。列舉項iOS的列舉關聯值是一個含有兩個字串元素的元組,而Android列舉項的關聯值是一個字串。下方的iOSValueandroidValue是兩個計算屬性,用來返回相關列舉項的關聯值。

當然,我們使用 if-case-let語句來獲取相關的列舉關聯值,具體如下所示。

  

當然,我們還可以對 “==”運算子進行過載,讓其支援上述定義的列舉型別的比較。下方主要還是Switch的使用,當然,之前我們也針對過Switch單獨進行過講解,下方就是Switch對元組的匹配,並且在相應的case中獲取列舉的關聯值,如下所示。

  

下方就是上述列舉的使用與輸出結果,如下所示:

  

三、ReactiveSwift中的Event的實現

接下來我們就來分析一下ReactiveSwift框架中的Event列舉的程式碼實現。我先看其原始碼,然後再看其使用方式。

1、Event中的事件型別

下方截圖中就是Event列舉型別中所包含的所有列舉項。從下方程式碼中我們可以看出,Event後方跟了兩個泛型,一個是Value,另一個是遵循Swift.Error協議的Error泛型。然後緊跟著的是Event列舉中的幾個事件型別。下方是對這幾種型別的介紹:

  • value: 用來關聯訊號量所傳送過來的值,該值的型別就是上面定義的Value泛型。
  • failed: 表示因錯誤而被迫中止的事件,其關聯值是相關的錯誤資訊。
  • completed: 該事件是完成事件,也就是所有的東西都success,正常終止。
  • interrupted: 該事件表示被迫中斷的事件,也就是沒有達到預期效果,被迫中止。

  

2、Event中的 isCompleted 和 isTerminating計算屬性

這兩個屬性是計算屬性,下方是其實現程式碼。isCompleted 用來判斷該事件是否是正常完成的事件,而isTerminating主要用來判斷事件是否已經終止,當然其中包括異常終止。當然這兩個計算屬性也是比較簡單的,就是根據不同的條件返回不同Bool值即可。

  

3、Event中的 value 和 error 計算屬性

下方這兩個也是計算屬性,主要是通過 if-case-let 語句來獲取列舉的關聯值,並與相應的計算屬性進行關聯。value屬性則用來獲取列舉項.value所關聯的值。而error則用來獲取列舉項.failed所關聯的值。具體程式碼如下所示。

  

4、Even計算屬性的測試

接下來,我們就對上述的計算屬性進行測試。下方這段程式碼就是對上述計算屬性的測試。首先我們建立了一個型別為 Event<Int, NSError>型別的事件。該事件所關聯的值為100,然後我們輸出計算屬性value、isTerminating、isCompleted計算屬性的值進行列印,具體列印結果如下所示。

然後我們又建立了一個錯誤型別的事件errorEvent。並給該列舉項關聯一個NSError型別的錯誤物件。然後對error、isTerminating、isCompleted的值進行列印。從列印結果可以看出isTerminating為true,說明是終止事件,而isCompleted為false,則說明是非正常終止。

  

5、Event中的map函式

在Event列舉中,主要有兩個map函式,一個是map<U>()泛型函式。另一個是mapError<F>()泛型函式。因為mapError<F>()函式的實現與map<F>()函式的實現極為相似,我們此處就以mapError<U>()泛型函式為例。也就是下方這個完整的函式。

map<U>()函式是一個泛型函式,在函式名map後緊跟的<U>就是我們定義的泛型。而該函式的引數是一個閉包 f, 該閉包的型別為 (Value) -> U。也就是說該閉包的有一個Value型別的引數,並且返回一個U型別的返回值。map<U>()這個函式的返回值是一個新的事件,該事件的型別為Event<U, Error>。經過這麼一分析,map<U>()函式就是將當前的 Event<Value, Error> 型別的事件對映成Event<U, Error>型別的事件。當然此處的Value和U都是泛型,當然如果換成具體的引數的話,也就是說一個 Event<Int, Error> 型別的引數可以通過下方的方法來對映成 Error<String, Error> 型別的事件。

下方我們需要主要的是返回值 .value( f(value) ) 這句話,.value()的關聯值是f(value)這個閉包所返回的值,而f(value)這個閉包的引數是之前事件所繫結的值。而f(value)所返回的值就是要對映的結果型別。f()的閉包體由使用者來提供,也就是說使用者可以自定義對映規則。

  

6、map函式的測試用例

接下來我們來看一下Map函式的使用方式。下方程式碼段就是Map函式的測試用例以及執行結果。首先我們建立了一個型別為 Event<Int, NSError> 型別的事件,然後該事件的value值為100。 然後我們呼叫map函式將 Event<Int, NSError> 型別對映成 Event<String, NSError>型別。然後map函式後邊跟隨的尾隨閉包就是我們的對映規則。你可以在該閉包中新增任意的對映規則,將原來的值轉換成你想要的值。

  

mapError<F>()函式的實現以及使用方式,與上述函式類似。接下來我們就來看一下mapError<F>()函式的使用方式。首先我們定義了兩個錯誤類,一個是MyError另一個是MyError1。並且定義了一個Event<Int, MyError> 型別的錯誤事件,然後呼叫 mapError<F>()函式將其轉換成 Event<Int, MyError1> 型別的事件,當然呼叫時提供的閉包仍然是對映規則。具體如下所示。

  

Event列舉中還有對 == 號運算子的過載,使Event型別的引數支援 == 運算子。其中還有一個將事件型別轉換成description描述字串的 extension。因為其內容比較簡單,在此就不做過多贅述了。

四、ReactiveSwift中的Observer

聊完Event的實現,我們來看一下Observer類的實現。Observer的主要職能是對Event進行使用,也就是Observer可以呼叫自己的方法來發送Event中所提供的各種事件的。下方就是對Observer類的詳細解析。

1、Observer類中屬性以及構造器的解析

接下來我們來看一下Observer類中所宣告的屬性以及構造器。首先我們注意到,Observer類也是也一個泛型類,在Observer類名後方分別跟著 ValueError: Swift.Error兩個泛型。這兩個泛型分別與Event後邊的泛型相對應,Value就是事件所關聯值的型別,而Error就是發生錯誤時錯誤的型別。

緊接著是聲明瞭一個 (Event<Value, Error>) -> Void 的閉包型別,並且為該型別聲明瞭一個Action的別名。然後使用這個Action的別名聲明瞭一個action的不可變屬性。而Observer的構造器的引數就是一個型別為(Event<Value, Error>) -> Void 的閉包。

Observer還聲明瞭一個便利構造器。該便利構造器有四個可選型別的引數,每個引數的型別都是一個閉包。這四個可選型別的閉包引數分別與Event中的四種事件相對應,在便利構造器中呼叫Observer的構造器時,提供了Action閉包的閉包體,在Action閉包體中,根據具體的事件型別來執行便利構造器引數所提供的相應閉包引數。當然便利構造器的閉包引數由Observer的使用者所提供,用來回調相應事件中的值。

  

根據上面的原始碼我們不難看出,在初始化Observer的物件時,我們可以呼叫構造器,也可以呼叫便利構造器來進行初始化。當然,還是推薦使用便利構造器來例項化Observer類的例項。下方第一個就是使用的便利構造器來例項化Observer的,並且在呼叫是提供了四個閉包回撥,來分別處理Observer發來的不同事件。

當然你也可以直接呼叫 Observer所提供的構造器,也就是直接為Action閉包賦值。第二段程式碼中的尾隨閉包就是Action的閉包體,當然我們需要自己處理Action針對不同事件是所給出的處理塊。

  

2、Observer中傳送事件的方法sendXXX()

接下來我們就來看一下Observer中傳送各種事件的方法,當然Event有四種事件型別,那麼Observer中也就是是4個傳送事件的方法了。下方程式碼片段就是Observer中傳送事件的方法,從下方的方法中我們不難看出,傳送事件其實就是對 action閉包的呼叫,並且傳入相應的事件。在呼叫 action 閉包時,就會執行我們所提供的或者遍歷構造器中所提供的閉包體,將傳送的事件回調出去。

  

3、sendXXX()方法的測試用例

上面我們已經通過Observer的構造器和便利構造器例項化兩個例項,接下來我們就呼叫這些例項所對應的send方法。下方程式碼片段就是對相應Observer例項的相關send方法的執行。

  

下方就是上述測試用例的執行結果

  

五、Observer工作的流程圖

看完上述程式碼,因為閉包回撥會導致一些程式碼的執行流程已經呼叫關係不太容易理解,解析來我們就來畫一個圖來簡述Observer的具體工作過程。下方程式碼就是上述測試用以的執行以及呼叫的過程。

下方是一個完整的程式執行過程,輸入->處理->輸出

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