RxSwift簡介和吐槽
RxSwift 是 ReactiveX 系列的 Swift 版本泣港,如果你之前用過 ReactiveCocoa(RAC) 的話,想必對 Functional Reactive Programming(FRP曹洽,函數(shù)響應式編程)這個概念不會陌生致燥,是的瓤檐,RxSwift 同樣是一個 FRP 框架赂韵。值得一提的是,RAC 的README 里有這么幾句話:
ReactiveCocoa was originally inspired, and therefore heavily influenced, by Microsoft’s Reactive Extensions (Rx) library.
Although ReactiveCocoa was started as an Objective-C framework, as of version 3.0, all major feature development is concentrated on the Swift API.
第一句是說挠蛉,RAC 是受微軟的 Reactive Extensions 啟發(fā)的祭示,所以也受了 Reactive Extensions 很大的影響(當然,之后它羅列了 RAC 跟 Rx 的一些差別谴古,并且安利 iOS 開發(fā)者來使用 RAC)质涛。第二句是說,雖然 RAC 是作為一個 OC 框架出道的掰担,但是從3.0版本開始汇陆,所有主要特性的開發(fā)都已經(jīng)以 Swift API 為重心了。也就是說带饱,今后不管是不是下雨天毡代,RAC 都跟 Swift 更配哦阅羹。
如果你是一個使用 Swift 的 iOS 開發(fā)者,并且對無處不在的 OOP 感到了厭倦教寂,想要打開新世界的大門看看的話捏鱼,這兩個框架都是可以選擇的。不過由于我感興趣的是框架的具體實現(xiàn)酪耕,相比于 OC 我又更喜歡 Swfit导梆,所以挑了純 Swift 實現(xiàn)的 RxSwift 來看。
An API for asynchronous programming
with observable streams
上面這句話來自 Rx 的官網(wǎng)迂烁,看到streams
我立馬就想到了《 SICP》第三章的“流”看尼,加之 Swift 對函數(shù)式編程的支持又很好,所以我原以為 RxSwift 的內(nèi)部實現(xiàn)會用延遲的表來作為信號流盟步,用流來表示某個對象順序狀態(tài)的時間史狡忙,這樣一切都是函數(shù),沒有狀態(tài)變化址芯,也就不需要同步機制來保證線程安全了。事實證明我還是圖樣窜觉!RxSwift 內(nèi)部還是有各種類各種繼承谷炸,當然也有各種同步機制:自旋鎖、遞歸鎖禀挫、原子操作……說好的 functional 呢旬陡?只有暴露給使用者的 API 是functional 么?這一開始讓我有些失望语婴,不過后來發(fā)現(xiàn)整個框架還是用到了大量函數(shù)式特性的描孟,只是不像我所想的那么純粹(一個 pure functional 的框架大概也很難真正流行起來……)。
好了吐槽完畢砰左,我們再看一句官網(wǎng)的介紹:
ReactiveX is a combination of the best ideas from
the Observer pattern, the Iterator pattern, and functional programming
這句話的意思是說 Rx 是結(jié)合了觀察者模式匿醒、迭代器模式和函數(shù)式編程這三種最佳思維的產(chǎn)物。雖然它沒有如我所想用純函數(shù)式的代碼實現(xiàn)缠导,不過用到了“流”的思想倒也是實實在在的廉羔。目前,我只看了一小部分代碼僻造,大致清楚了觀察者模式部分的實現(xiàn)憋他,下面就跟大家分享一下。
Observable 和 Observer
RxSwift 項目內(nèi)部有個 Rx.playground髓削,在介紹頁面上有這么一句話:
The key to understanding RxSwift is in understanding the notion of Observables. Creating them, manipulating them, and subscribing to them in order to react to changes.
這句話是說竹挡,理解 RxSwfit 的關(guān)鍵是理解“被觀察者”這個概念,創(chuàng)造它們立膛,操縱它們揪罕,然后訂閱它們來響應變化。Observable
的重要性可見一斑。讓我們來看一個使用Observable
的實例:
empty
empty creates an empty sequence. The only message it sends is the .Completed message.
介紹了一個 empty 函數(shù)耸序,它會創(chuàng)建一個空的 sequence(翻譯成序列的話總感覺會引起誤會)忍些,這個 sequence 只會發(fā)送 .Completed 這一個消息,示例代碼如下:
let emptySequence: Observable<Int> = empty()
let subscription = emptySequence
.subscribe { event in
print(event)
}
上述代碼通過empty
函數(shù)得到了一個Observable<Int>
坎怪,好現(xiàn)在去看看empty
:
public func empty<E>() -> Observable<E> {
return Empty<E>()
}
果然是 OOP 外面套 FP 的皮啊罢坝,沒有這個 empty 函數(shù)我們照樣可以直接let emptySequence = Empty<Int>()
來得到一個Observable<Int>
嘛,那現(xiàn)在就去看看這個Empty
是個什么鬼:
class Empty<Element> : Producer<Element> {
override init() {
}
override func run<O : ObserverType where O.E == Element>(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable {
observer.on(.Completed)
return NopDisposable.instance
}
}
乍一看這個類還是比較容易懵的搅窿。這個空構(gòu)造器是什么意思嘁酿?好吧大概是為了初始化的時候避免調(diào)用父類構(gòu)造器,就是確保什么都不做男应。然后下面這個 run 函數(shù)就令人費解了闹司,這一堆參數(shù),還有這個Disposable
是什么沐飘?其實如果是寫過 C# 的朋友游桩,一定覺得這個Disposable
非常熟悉,沒錯耐朴,它是一個協(xié)議(似乎微軟系的接口比較喜歡用形容詞借卧,用able結(jié)尾的很多),跟 C# 中用來顯式釋放資源的IDisposable
接口類似:
/**
類似 C# 中的 IDisposable 接口筛峭,用來釋放資源铐刘。
由于 Swift 使用 ARC,所以 dispose 方法大部分時候只是取消對某個資源的引用,
譬如 resource = nil
*/
public protocol Disposable {
/**
Dispose resource.
*/
func dispose()
}
由于這篇文章重點在于觀察者模式影晓,所以我想先放下Disposable
相關(guān)的東西不談镰吵,因為涉及資源的保存釋放有一些線程相關(guān)的操作,挺麻煩的挂签,但這些跟觀察者模式并沒有什么關(guān)系疤祭。基于此饵婆,我把 RxSwfit 中跟empty
画株、just
相關(guān)的一些類稍微簡化了一下,去掉了Disposable
相關(guān)的一些內(nèi)容啦辐,然后加了點注釋谓传,放到一起之后empty
、just
這幾個例子還是都能正常運行芹关。
好的续挟,簡化后Empty
類變成了這樣:
class Empty<Element> : Producer<Element> {
override func run<O : ObserverType where O.E == Element>(observer: O) {
// 觀察者訂閱了一個完成信號
observer.on(.Completed)
}
}
好,我們已經(jīng)得到一個Empty
的實例侥衬,接下來我們要調(diào)用它的subscribe
方法诗祸,這個subscribe
方法的參數(shù)類型是(Event<E>) -> Void
跑芳,是一個閉包類型。我們在ObservableType
協(xié)議的擴展里找到了符合條件的subscribe
方法:
extension ObservableType {
func subscribe(on: (event: Event<E>) -> Void) {
// 構(gòu)造一個匿名觀察者直颅,把參數(shù) on 賦值給這個匿名觀察者的 eventHandler博个,
// 相當于 let observer = AnonymousObserver(on)
let observer = AnonymousObserver { e in
on(event: e)
}
self.subscribeSafe(observer)
}
subscribe
方法接受了閉包之后,先構(gòu)造了一個匿名觀察者功偿,event
這個閉包作為構(gòu)造器的參數(shù)傳給了observer
盆佣。看一下AnonymousObserver
:
class AnonymousObserver<ElementType> : ObserverBase<ElementType> {
typealias Element = ElementType
typealias EventHandler = Event<Element> -> Void
private let eventHandler : EventHandler
init(_ eventHandler: EventHandler) {
// 資源情況追蹤(為了開發(fā)期解決內(nèi)存泄漏問題吧)
#if TRACE_RESOURCES
// 原子操作:resourceCount 加1
OSAtomicIncrement32(&resourceCount)
#endif
self.eventHandler = eventHandler
}
// onCore 會被 on 調(diào)用(on 繼承自父類)
override func onCore(event: Event<Element>) {
return self.eventHandler(event)
}
#if TRACE_RESOURCES
deinit {
// 原子操作:resourceCount 減1
OSAtomicDecrement32(&resourceCount)
}
#endif
}
忽略追蹤內(nèi)存情況的代碼不看械荷,這個類主要就是在構(gòu)造器中接受一個閉包共耍,然后賦值給私有屬性eventHandler
,然后在onCore
方法中吨瞎,eventHandler
會被調(diào)用痹兜。可是我們之前看Empty
類的時候已經(jīng)知道颤诀,觀察者的on
方法會在run
中被調(diào)用字旭,并不是這個onCore
啊,看來還得到父類ObserverBase
中看看:
class ObserverBase<ElementType>: ObserverType {
typealias E = ElementType
var isStopped: Int32 = 0
init() {
}
func on(event: Event<E>) {
switch event {
case .Next:
if isStopped == 0 {
onCore(event)
}
// 一旦出現(xiàn)一次 Error 或 Completed 事件崖叫,之后也不會再執(zhí)行 onCore 了
case .Error, .Completed:
// OSAtomicCompareAndSwap32:比較和交換的原子操作谐算,如果 isStopped == 0,則 isStoppend = 1,返回 true,否則返回 false
if !OSAtomicCompareAndSwap32(0, 1, &isStopped) {
return
}
onCore(event)
}
}
// 會在子類中重寫
func onCore(event: Event<E>) {
abstractMethod()
}
}
好了归露,這下清楚了,onCore
會被on
調(diào)用斤儿【绨回到subscribe
中繼續(xù)往下走,得到了observer
這個實例之后往果,它將會一路被作為參數(shù)傳遞疆液。先是調(diào)用self.subscribeSafe(observer)
,observer
被傳遞給subscribeSafe
方法陕贮,這個方法同樣在ObserverType
的extension
中:
func subscribeSafe<O: ObserverType where O.E == E>(observer: O) {
// 會調(diào)用被子類實現(xiàn)的的 subscribe 方法
self.subscribe(observer)
}
在subscribeSafe
中最后又會調(diào)用subscribe
方法堕油,不過這個subscribe
的參數(shù)是ObserverType
的實現(xiàn)類,不是閉包肮之,所以這是一個重載方法掉缺。它的聲明在協(xié)議ObservableType
中:
protocol ObservableType {
/**
hack: 因為 Swift 中沒有范型協(xié)議,只能在協(xié)議中聲明一個別名戈擒,
然后將實現(xiàn)類聲明為范型類眶明,再將傳入的范型名命名為 E(如 typealias E = Element)
在接受范型參數(shù)的地方這樣使用:
func demo<O : ObservableType where O.E == Int>(ob: O)
大致與 C# 中 void demo(ObservableType<int> ob) 作用相同
*/
typealias E
func subscribe<O: ObserverType where O.E == E>(observer: O)
}
我們發(fā)現(xiàn)這個方法沒有出現(xiàn)在Empty
類中,只能沿著Empty
的繼承樹往上找筐高,在Empty
的父類Producer
中可以找到它的實現(xiàn):
class Producer<Element> : Observable<Element> {
// 會被 ObserverType 的 extension 方法 subscribeSafe 調(diào)用
override func subscribe<O : ObserverType where O.E == E>(observer: O) {
// 會有一些關(guān)于資源釋放以及線程相關(guān)的操作
// ……
run(observer)
}
func run<O : ObserverType where O.E == Element>(observer: O) {
abstractMethod()
}
}
subscribe
方法會調(diào)用run
方法搜囱,但是這個run
方法里面調(diào)用了abstractMethod
丑瞧,我們來看看它是什么:
@noreturn func abstractMethod() -> Void {
fatalError("Abstract method")
}
一旦調(diào)用這個方法就會觸發(fā)致命錯誤fatalError
,所以run
必須被子類重寫蜀肘,否則程序會終止绊汹。我猜是因為 Swift 中沒有抽象類和抽象方法的概念,不能在函數(shù)前加 abstract 強制子類重寫該方法扮宠,只能用這種不重寫就終止的方式來模擬抽象方法西乖。既然這樣我們就來看看子類中的run
方法:
class Empty<Element> : Producer<Element> {
// run 會在父類中被 subscribe 方法調(diào)用
override func run<O : ObserverType where O.E == Element>(observer: O) {
// 觀察者訂閱了一個完成信號
observer.on(.Completed)
}
}
class Just<Element>: Producer<Element> {
let element: Element
init(element: Element) {
self.element = element
}
override func run<O : ObserverType where O.E == Element>(observer: O) {
observer.on(.Next(element))
observer.on(.Completed)
}
}
如上是Empty
和Just
的兩個run
實現(xiàn),在Empty
中涵卵,會調(diào)用傳遞過來的observer
的on
方法一次浴栽,并將.Completed
作為參數(shù)。我們知道這個on
方法其實就是最開始我們調(diào)用subscribe
方法時傳入的那個閉包轿偎,即{event in print(event)}
典鸡,所以最后就會打印出.Completed
。至于這個.Completed
么坏晦,顯然是個枚舉萝玷,它是一個Event
類型:
enum Event<Element> {
case Next(Element)
case Error(ErrorType)
case Completed
}
而Just
的初始化函數(shù)會接受一個值并將其賦值給實例屬性element
,然后調(diào)用run
方法的時候昆婿,會調(diào)用傳遞過來的observer
的on
方法兩次球碉,一次以.Next(element)
為參數(shù),一次以.Completed
為參數(shù)表示結(jié)束仓蛆。就像這樣:
// MARK: - 調(diào)用
print("just observable demo:")
let justObservable = just(1)
justObservable.subscribe { event in
print(event)
}
print("----")
print("empty observable demo:")
let emptyObservable: Observable<Int> = empty()
emptyObservable.subscribe { event in
print(event)
}
輸出:
just observable demo:
Next(1)
Completed
----
empty observable demo:
Completed
有點繞對不對睁冬,主要是因為繼承太多,很多方法都要到父類中去找看疙。我簡化后的版本在這里豆拨,可能我說這么多還不如大家自己 clone 下來看一眼來得明白。
小結(jié)
因為代碼只看了個開頭能庆,所以我暫時還不能理解 RxSwift 中繼承層級這么多的必要性施禾。主要這畢竟是個重型的框架,我必須讀一點記錄一點搁胆,不然看到后面就忘了前面弥搞。要說目前為止有什么收獲么,大抵是如下幾點:
- 觀察者模式的 Swift 實現(xiàn)渠旁。
- 借助 typealias 模擬范型協(xié)議的具體做法攀例。
- 借助 fatalError 模擬抽象方法的具體做法。
- 自旋鎖顾腊、遞歸鎖和兩種原子操作的用法肛度。