當前位置:
首頁 > 知識 > ReactiveCocoa源碼解析(一)Event與Observer代碼實現

ReactiveCocoa源碼解析(一)Event與Observer代碼實現

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

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

拋去「響應式編程」的概念,ReactiveCocoa的本質還是對「觀察者模式」的使用,關於觀察者模式,請參考之前的博客《設計模式(二):自己動手使用「觀察者模式」實現通知機制》。也可以說ReactiveCocoa是「觀察者模式」應用中比較牛X的一個框架。當然,框架在編碼實現時還用到了其他設計模式,在解析到相關內容時,我們在對其進行概述。

當然,本篇博客是對ReactiveSwift源碼的解析,也就是說你可以在你的工程中僅僅的引入 ReactiveSwift框架 ,GitHub地址為:https://github.com/ReactiveCocoa/ReactiveSwift.git,至於如何將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協議的類型,所以聲明該泛型的形式為。聲明完該泛型後,在類中我們就可以想使用普通類型那樣來使用該泛型了。

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

ReactiveCocoa源碼解析(一)Event與Observer代碼實現

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

ReactiveCocoa源碼解析(一)Event與Observer代碼實現

二、Swift中的枚舉

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

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

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

ReactiveCocoa源碼解析(一)Event與Observer代碼實現

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

ReactiveCocoa源碼解析(一)Event與Observer代碼實現

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

ReactiveCocoa源碼解析(一)Event與Observer代碼實現

三、ReactiveSwift中的Event的實現

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

1、Event中的事件類型

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

  • value

    : 用來關聯信號量所傳送過來的值,該值的類型就是上面定義的Value泛型。

  • failed

    : 表示因錯誤而被迫中止的事件,其關聯值是相關的錯誤信息。

  • completed

    : 該事件是完成事件,也就是所有的東西都success,正常終止。

  • interrupted

    : 該事件表示被迫中斷的事件,也就是沒有達到預期效果,被迫中止。

ReactiveCocoa源碼解析(一)Event與Observer代碼實現

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

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

ReactiveCocoa源碼解析(一)Event與Observer代碼實現

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

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

ReactiveCocoa源碼解析(一)Event與Observer代碼實現

4、Even計算屬性的測試

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

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

ReactiveCocoa源碼解析(一)Event與Observer代碼實現

5、Event中的map函數

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

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

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

ReactiveCocoa源碼解析(一)Event與Observer代碼實現

6、map函數的測試用例

接下來我們來看一下Map函數的使用方式。下方代碼段就是Map函數的測試用例以及運行結果。首先我們創建了一個類型為 Event類型的事件,然後該事件的value值為100。 然後我們調用map函數將Event類型映射成Event類型。然後map函數後邊跟隨的尾隨閉包就是我們的映射規則。你可以在該閉包中添加任意的映射規則,將原來的值轉換成你想要的值。

ReactiveCocoa源碼解析(一)Event與Observer代碼實現

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

ReactiveCocoa源碼解析(一)Event與Observer代碼實現

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) -> Void的閉包類型,並且為該類型聲明了一個Action的別名。然後使用這個Action的別名聲明了一個action的不可變屬性。而Observer的構造器的參數就是一個類型為(Event) -> Void的閉包。

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

ReactiveCocoa源碼解析(一)Event與Observer代碼實現

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

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

ReactiveCocoa源碼解析(一)Event與Observer代碼實現

2、Observer中發送事件的方法sendXXX

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

ReactiveCocoa源碼解析(一)Event與Observer代碼實現

3、sendXXX方法的測試用例

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

ReactiveCocoa源碼解析(一)Event與Observer代碼實現

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

ReactiveCocoa源碼解析(一)Event與Observer代碼實現

五、Observer工作的流程圖

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

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

ReactiveCocoa源碼解析(一)Event與Observer代碼實現

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

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 達人科技 的精彩文章:

NetCore1.1+Linux部署初體驗
基於servlet和ajax的聊天室
解析器(一):分隔符指導
vue-router路由參數刷新消失的問題
沒有main方法真的不能執行代碼了嗎?

TAG:達人科技 |

您可能感興趣

android 訪問webservice(解析一行代碼實現)
網關 Spring-Cloud-Gateway 源碼解析——路由之RouteDefinitionLocator一覽
Istio技術與實踐02:源碼解析之Istio on Kubernetes 統一服務發現
Bayesian Personalized Ranking 演算法解析及Python實現
AtomicInteger 源碼解析
技術解析系列PouchContainer Goroutine Leak 檢測實踐
使用 Python的urlliib.parse 庫解析 URL
druid-spring-boot-starter源碼解析
精讀解析 《When Sophie Gets Angry》
Photoshop詳細解析CameraRaw插件使用
osg例子解析-osgsimpleshaders
藉助Jackson的JsonTypeInfo註解實現多態類的解析
Google 開源 robots.txt 解析器,推動 REP 標準化
Marvell第二代ARM處理器ThunderX2解析:不遜Intel至強
視覺中國官網vcg.com域名狀態變為clientHold(暫停解析)
Moto E5 Play Android Go手機發布:真上古解析度
Burberry 和 Vivienne Westwood 的合作系列已經開售,我們為你帶來完整解析
Burberry和Vivienne Westwood 的合作系列已經開售,我們為你帶來完整解析
長文解析:Gardenscapes的成功和Playrix的轉型分析
新一代 Apple Watch Series 4 將提升解析度