前言
我們?cè)?iOS 開發(fā)過程中,幾乎無時(shí)無刻都要面對(duì)異步事件的處理。例如,按鍵點(diǎn)擊、數(shù)據(jù)保存、、音頻后臺(tái)播放、交互動(dòng)畫展示。這些事件并不具備特定時(shí)序性,甚至它們可能同時(shí)發(fā)生。
雖然 Apple 提供了通知、代理、GCD、閉包等異步機(jī)制,但是這些機(jī)制缺乏一個(gè)統(tǒng)一的抽象表述。另外,這些機(jī)制在處理共享的可變數(shù)據(jù)或狀態(tài)時(shí)不夠清晰簡(jiǎn)練。當(dāng)然,這并不是說編寫優(yōu)雅的異步代碼不現(xiàn)實(shí)。畢竟與其他平臺(tái)相比 iOS 的異步機(jī)制還是很強(qiáng)大的。
幸運(yùn)的是,我們能夠通過 RxSwift 優(yōu)雅的處理異步代碼。
至于 RxSwift 的優(yōu)勢(shì)以及為什么要使用它,詳見文檔。這里就不廢話了。
RxSwift 簡(jiǎn)介
其實(shí)響應(yīng)式編程并不是一個(gè)什么新的概念,只不過是最近幾年受到了開發(fā)者更多的關(guān)注。它最早由巨硬提出,主要的目的是為了應(yīng)對(duì)復(fù)雜的 UI 異步事件和應(yīng)用實(shí)時(shí)響應(yīng)。社區(qū)中也已經(jīng)有了各種語(yǔ)言版本的響應(yīng)式編程實(shí)現(xiàn),包括:RxJS、RxKotlin、Rx.NET、RxScala、RxSwift。這些類庫(kù)僅僅只是實(shí)現(xiàn)方式存在差異,所以開發(fā)者在討論應(yīng)用邏輯時(shí)不會(huì)存在溝通障礙。
RxSwift 作為 Swift 語(yǔ)言的響應(yīng)式編程實(shí)現(xiàn),它在傳統(tǒng)的命令式編程和純函數(shù)式編程中找到了一個(gè)很好的平衡點(diǎn)。通過使用不可變代碼定義異步處理輸入,RxSwift 以一種確定可組合的形式對(duì)事件做出響應(yīng)。
總的來說,RxSwift 有三個(gè)主要構(gòu)成部分:Observable、Operator、Scheduler 。下面我們就來一一介紹。
Observable
Observable<T>
類可以說是 RxSwift 整個(gè)框架的基石。它能異步的觸發(fā)一系列事件流并攜帶不可更改的狀態(tài)變量。簡(jiǎn)單來說就是:它能讓某個(gè)類的實(shí)例在一段時(shí)間內(nèi)實(shí)現(xiàn)對(duì)另一個(gè)實(shí)例對(duì)象值的觀察。例如:觀察者可以捕獲對(duì)所有可觀察對(duì)象觸發(fā)的事件,從而實(shí)現(xiàn) UI 的實(shí)時(shí)更新或者是數(shù)據(jù)的實(shí)時(shí)處理。
其中 Observable<T>
類遵循了 ObservableType 協(xié)議。另外,Observable 對(duì)象所能觸發(fā)的事件只有以下三種:
- next 事件:該事件在觸發(fā)時(shí)會(huì)將可觀察對(duì)象的最新值傳遞給觀察者。
- completed 事件:該事件意味著可觀察對(duì)象的生命周期正常結(jié)束不會(huì)在繼續(xù)觸發(fā)事件。
- error 事件:該事件表明可觀察對(duì)象出現(xiàn)了錯(cuò)誤導(dǎo)致生命周期異常終止。
對(duì)于一個(gè)可觀察的整型變量來說,異步環(huán)境下它所觸發(fā)的事件可以在時(shí)間線上被描繪成這樣一個(gè)事件序列:
另外,我們可以對(duì)這三類事件進(jìn)行組合從而實(shí)現(xiàn)更為復(fù)雜的業(yè)務(wù)邏輯。與此同時(shí),我們還可以使用該機(jī)制輕松實(shí)現(xiàn)代碼解耦和多個(gè)對(duì)象間數(shù)據(jù)傳遞,無需編寫代理或者閉包代碼。
這里,我們還有一點(diǎn)值得注意。那就是可觀察序列其實(shí)有兩種類型。
有限觀察序列( Finite observable sequences )
該序列是指那些最后會(huì)以 completed 或者 error 事件終極生命周期的可觀察對(duì)象。最典型的例子就是,通過 API 進(jìn)行網(wǎng)絡(luò)請(qǐng)求:
- 開始數(shù)據(jù)請(qǐng)求并準(zhǔn)備進(jìn)行數(shù)據(jù)接收。
- 接收到服務(wù)端響應(yīng)開始接收數(shù)據(jù)。
- 如果服務(wù)器或者網(wǎng)絡(luò)發(fā)生故障則關(guān)閉請(qǐng)求并觸發(fā)錯(cuò)誤處理。
- 如果一切正常則對(duì)請(qǐng)求數(shù)據(jù)進(jìn)行處理和分析。
下面是一個(gè)文件下載請(qǐng)求的 Rx 范式的代碼:
1
2
3
4
5
6
7
|
API.download(file: "http://www..." ) .subscribe( onNext: { data in append data to temporary file }, onError: { error in display error to user }, onCompleted: { use downloaded file }) |
這段代碼中 API.download (file:)
函數(shù)會(huì)創(chuàng)建一個(gè) Observable<Data>
實(shí)例對(duì)象,并且在整個(gè)數(shù)據(jù)接收過程中會(huì)不斷的觸發(fā) next 事件。然后,我們?cè)?next 事件中會(huì)將這些片段數(shù)據(jù)保存到臨時(shí)文件中。如果此過程出現(xiàn)錯(cuò)誤的話,我們會(huì)將錯(cuò)誤信息展示給用戶。如果一切順利我們會(huì)將臨時(shí)文件保存到設(shè)備中。最后在下載完成后,我們可以在 completed 進(jìn)行下一步的邏輯處理。
無限觀察序列( Infinite observable sequences )
與網(wǎng)絡(luò)任務(wù)不同的是,UI 以及交互事件是無限觀察序列。它們并不存在一個(gè)明確的生命周期終結(jié)點(diǎn)。例如,針對(duì)可能的設(shè)備方向旋轉(zhuǎn),我們需要實(shí)時(shí)進(jìn)行布局修改。而設(shè)備的方向旋轉(zhuǎn)本身是隨機(jī)發(fā)生的并且與應(yīng)用本身具有同樣的生命周期。因此 Rx 也需要一種機(jī)制支持這種無限觀察序列。
針對(duì)這種情況,在 RxSwift 中我們可以通過以下代碼來應(yīng)對(duì):
1
2
3
4
5
6
7
8
|
UIDevice.rx.orientation.subscribe(onNext: { current in switch current { case .landscape: re-arrange UI for landscape case .portrait: re-arrange UI for portrait } }) |
操作符
ObservableType 以及 Observable 類的實(shí)現(xiàn)中都包含大量的異步處理方法,這些方法也被稱為操作符。由于這些操作符只是進(jìn)行異步輸入處理并產(chǎn)生對(duì)應(yīng)輸出,所以它并不會(huì)對(duì)應(yīng)用產(chǎn)生多余的副作用。另外,因?yàn)椴僮鞣g的高度解耦所以我們很容易對(duì)它進(jìn)行組合以期實(shí)現(xiàn)復(fù)雜的功能。
例如,對(duì)于上面的設(shè)備方向旋轉(zhuǎn),我們可以對(duì)所有的情況進(jìn)行過濾然后對(duì)部分值進(jìn)行進(jìn)一步處理。
1
2
3
4
5
6
7
8
9
10
|
UIDevice.rx.orientation .filter { value in return value != .landscape } .map { _ in return "Portrait is the best!" } .subscribe( onNext: { string in showAlert(text: string) }) |
上面的代碼中,我們首先會(huì)將所有 .landscape 方向過濾掉不做任何處理。然后,我們?cè)賹⑹O碌?portrait 轉(zhuǎn)化為字符串 Portrait is the best! 。整個(gè)處理流程大致如下:
這種函數(shù)式的操作符讓我們可以靈活的組合出更強(qiáng)大的功能。
Scheduler
Schedulers 是一個(gè)與 GCD 相對(duì)應(yīng)的概念,只不過前者使用起來更為方便。RxSwift 中預(yù)定義的 Schedulers 足夠開發(fā)者應(yīng)對(duì)絕大多數(shù)的編程場(chǎng)景。
例如,我們可以使用串型序列 SerialDispatchQueueScheduler 來處理 next 事件,通過 ConcurrentDispatchQueueScheduler 運(yùn)行并行文件下載任務(wù),通過 OperationQueueScheduler 運(yùn)行一個(gè) NSOperationQueue 操作隊(duì)列。甚至你可以在同一個(gè)觀察對(duì)象的不同任務(wù)中使用不同的 Schedulers 類型,如下圖:
我們將左側(cè)的任務(wù)用不同的顏色加以區(qū)分,然后在右側(cè)任務(wù)被拆分為不同的步驟并且放在不同 Schedulers 中。例如,network subscription 任務(wù)就被拆分為三個(gè)步驟并依次放入了 Custom NSOperation Scheduler 、Background Concurrent Scheduler、Main Thred Serial Scheduler 。
補(bǔ)充
值得注意的是, RxSwift 并沒有對(duì)客戶端的應(yīng)用架構(gòu)作出硬性規(guī)定。這意味著,我們可以在已有項(xiàng)目中引入 RxSwift 進(jìn)行響應(yīng)式編程實(shí)踐。當(dāng)然已有框架中必定存在一個(gè)最適合 RxSwift 的,而它就是 MVVM。因?yàn)樵?MVVM 中我們可以將 VM 中的部分屬性直接與 UI 進(jìn)行綁定。
另外,對(duì)于 iOS 編程來說僅僅有 RxSwift 是遠(yuǎn)遠(yuǎn)不夠的。RxSwift 只是 Swift 語(yǔ)言的響應(yīng)式實(shí)現(xiàn),我們還需要一種 Cocoa 層面的實(shí)現(xiàn)。好在這里存在 RxCocoa 作為 UIKit 的響應(yīng)式補(bǔ)充。前面設(shè)備方向代碼 UIDevice.rx.orientation
就是 RxCocoa 的應(yīng)用 。
總結(jié)
作為系列開篇,本文介紹了 RxSwift 的一些基本理念和構(gòu)成,更多相關(guān)的內(nèi)容將會(huì)在后面帶來。
好了,以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作能帶來一定的幫助,如果有疑問大家可以留言交流,謝謝大家對(duì)服務(wù)器之家的支持。
原文鏈接:https://bignerdcoding.com/archives/50.html