在 iOS 開發過程中,我們幾乎無時無刻都要面對非同步事件的處理。例如,按鍵點選、資料儲存、、音訊後臺播放、互動動畫展示。這些事件並不具備特定時序性,甚至它們可能同時發生。
雖然 Apple 提供了通知、代理、GCD、閉包等非同步機制,但是這些機制缺乏一個統一的抽象表述。另外,這些機制在處理共享的可變資料或狀態時不夠清晰簡練。當然,這並不是說編寫優雅的非同步程式碼不現實。畢竟與其他平臺相比 iOS 的非同步機制還是很強大的。
幸運的是,我們能夠通過 RxSwift 優雅的處理非同步程式碼。
至於 RxSwift 的優勢以及為什麼要使用它,詳見文件。這裡就不廢話了。
RxSwift 簡介
其實響應式程式設計並不是一個什麼新的概念,只不過是最近幾年受到了開發者更多的關注。它最早由巨硬提出,主要的目的是為了應對複雜的 UI 非同步事件和應用實時響應。社群中也已經有了各種語言版本的響應式程式設計實現,包括:RxJS、RxKotlin、Rx.NET、RxScala、RxSwift。這些類庫僅僅只是實現方式存在差異,所以開發者在討論應用邏輯時不會存在溝通障礙。
RxSwift 作為 Swift 語言的響應式程式設計實現,它在傳統的指令式程式設計和純函數語言程式設計中找到了一個很好的平衡點。通過使用不可變程式碼定義非同步處理輸入,RxSwift 以一種確定可組合的形式對事件做出響應。
總的來說,RxSwift 有三個主要構成部分:Observable、Operator、Scheduler 。下面我們就來一一介紹。
Observable
Observable
其中 Observable
- next 事件:該事件在觸發時會將可觀察物件的最新值傳遞給觀察者。
- completed 事件:該事件意味著可觀察物件的生命週期正常結束不會在繼續觸發事件。
- error 事件:該事件表明可觀察物件出現了錯誤導致生命週期異常終止。
對於一個可觀察的整型變數來說,非同步環境下它所觸發的事件可以在時間線上被描繪成這樣一個事件序列:
另外,我們可以對這三類事件進行組合從而實現更為複雜的業務邏輯。與此同時,我們還可以使用該機制輕鬆實現程式碼解耦和多個物件間資料傳遞,無需編寫代理或者閉包程式碼。
這裡,我們還有一點值得注意。那就是可觀察序列其實有兩種型別。
有限觀察序列( Finite observable sequences )
該序列是指那些最後會以 completed 或者 error 事件終極生命週期的可觀察物件。最典型的例子就是,通過 API 進行網路請求:
- 開始資料請求並準備進行資料接收。
- 接收到服務端響應開始接收資料。
- 如果伺服器或者網路發生故障則關閉請求並觸發錯誤處理。
- 如果一切正常則對請求資料進行處理和分析。
下面是一個檔案下載請求的 Rx 正規化的程式碼:
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:) 函式會建立一個 Observable 例項物件,並且在整個資料接收過程中會不斷的觸發 next 事件。然後,我們在 next 事件中會將這些片段資料儲存到臨時檔案中。如果此過程出現錯誤的話,我們會將錯誤資訊展示給使用者。如果一切順利我們會將臨時檔案儲存到裝置中。最後在下載完成後,我們可以在 completed 進行下一步的邏輯處理。
無限觀察序列( Infinite observable sequences )
與網路任務不同的是,UI 以及互動事件是無限觀察序列。它們並不存在一個明確的生命週期終結點。例如,針對可能的裝置方向旋轉,我們需要實時進行佈局修改。而裝置的方向旋轉本身是隨機發生的並且與應用本身具有同樣的生命週期。因此 Rx 也需要一種機制支援這種無限觀察序列。
針對這種情況,在 RxSwift 中我們可以通過以下程式碼來應對:
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 類的實現中都包含大量的非同步處理方法,這些方法也被稱為操作符。由於這些操作符只是進行非同步輸入處理併產生對應輸出,所以它並不會對應用產生多餘的副作用。另外,因為操作符之間的高度解耦所以我們很容易對它進行組合以期實現複雜的功能。
例如,對於上面的裝置方向旋轉,我們可以對所有的情況進行過濾然後對部分值進行進一步處理。
UIDevice.rx.orientation
.filter { value in
return value != .landscape
}
.map { _ in
return "Portrait is the best!"
}
.subscribe( onNext: { string in
showAlert(text: string)
})複製程式碼
上面的程式碼中,我們首先會將所有 .landscape 方向過濾掉不做任何處理。然後,我們再將剩下的 portrait 轉化為字串 Portrait is the best! 。整個處理流程大致如下:
這種函式式的操作符讓我們可以靈活的組合出更強大的功能。
Scheduler
Schedulers 是一個與 GCD 相對應的概念,只不過前者使用起來更為方便。RxSwift 中預定義的 Schedulers 足夠開發者應對絕大多數的程式設計場景。
例如,我們可以使用串型序列 SerialDispatchQueueScheduler 來處理 next 事件,通過 ConcurrentDispatchQueueScheduler 執行並行檔案下載任務,通過 OperationQueueScheduler 執行一個 NSOperationQueue 操作佇列。甚至你可以在同一個觀察物件的不同任務中使用不同的 Schedulers 型別,如下圖:
我們將左側的任務用不同的顏色加以區分,然後在右側任務被拆分為不同的步驟並且放在不同 Schedulers 中。例如,network subscription 任務就被拆分為三個步驟並依次放入了 Custom NSOperation Scheduler 、Background Concurrent Scheduler、Main Thred Serial Scheduler 。
補充
值得注意的是, RxSwift 並沒有對客戶端的應用架構作出硬性規定。這意味著,我們可以在已有專案中引入 RxSwift 進行響應式程式設計實踐。當然已有框架中必定存在一個最適合 RxSwift 的,而它就是 MVVM。因為在 MVVM 中我們可以將 VM 中的部分屬性直接與 UI 進行繫結。
另外,對於 iOS 程式設計來說僅僅有 RxSwift 是遠遠不夠的。RxSwift 只是 Swift 語言的響應式實現,我們還需要一種 Cocoa 層面的實現。好在這裡存在 RxCocoa 作為 UIKit 的響應式補充。前面裝置方向程式碼 UIDevice.rx.orientation 就是 RxCocoa 的應用 。
總結
作為系列開篇,本文介紹了 RxSwift 的一些基本理念和構成,更多相關的內容將會在後面帶來。
原文地址