iOS響應式程式設計:ReactiveCocoa vs RxSwift 選誰好

發表於2016-02-02

內容來自stack overflow的一個回答:ReactiveCocoa vs RxSwift – pros and cons?

要直接比較這兩個有點難。Rx 是 Reactive Extensions 的一部分,其他語言像C#, Java 和 JS 也有。Reactive Cocoa 受 Functional Reactive Programming(FRP) 啟發,但是在最近一段時間裡,他們提到也受到Reactive Extensions的啟發。最終結果就是一個從Rx借鑑了一些東西,但是有著源自FRP名聲的一個框架。

第一點要說明的事是無論是RAC還是Rx都不是真正意義上的Functional Reactive Programming。按照 What is (functional) reactive programming 裡的回答對於FRP概念的定義。有了這個認識,我們就可以從兩個框架如何處理subscribing/observing時的副作用(side effect)和一些其他的元件上兩個方面來比較。

我們來看看社群(community)和技術實現(extra-tech)。

RAC是一個已經有著3年曆史的專案,從Objective-C時期開始,後來從3.0開始支援了swift(可以通過bridge在OC下使用),接著就完全停止了在Objective-C上的維護。RxSwift專案的時間短一些只有幾個月(作者寫的時間是15年),但是社群似乎充滿了動力。關於RxSwift有一件重要的事是專案是按照 ReactiveX這個組織的規定下開發的,並且所有其他語言的Rx專案也是一樣。如果學會了如何使用RxSwift,再去學習Rx.Net, RxJava 或者 RxJS就是小菜一碟,只是語言語法上的差異。這真的就是learn once, apply everywhere.

再來看看技術實現。

Producing/Observing Entities

RAC 3.0主要有兩個實體,signal SignalProducer。第一個釋出事件無論是否有繫結訂閱者,後者要有一個訊號或者事件產生才會觸發。這兩個區別是為了區分冷訊號和熱訊號,也使很多開發者困惑。這就是他們處理副作用的一大區別。

在RxSwift,signal SignalProducer變成了 Observable,聽起來有點困惑,但是這兩個實體在Rx的世界裡是同一個東西。在RxSwift裡建立Observables不需要考慮是冷訊號還是熱訊號,一旦你理解了他們的工作原理就很容易掌握。再次說明 冷/熱(cold/hot/warm)訊號就是當你subscribing/observing 產生的副作用。

對於訂閱的概念兩者基本是一樣的。在RAC裡有一點小的區別,RAC可以中斷一個事件當訊號被 disposed,即使在事件傳送完成訊號之前。總結一下兩者都有的以下事件:

  • Next
    處理新收到的值
  • Error
    處理一個錯誤,結束整個流,對所有的觀察者取消訂閱
  • Complete
    標記整個流已經完成,取消所有觀察者的訂閱
    另外RAC會在收到一個disposed Signal後中斷,即使沒有收到complete或者error。

Manually Writing

在RAC中,Signal/SignalProducer都是隻讀的實體,他們不能從外部被改變,RxSwift中的Observable也是如此。如果要把Signal/SignalProducer改變成可以手動改寫的實體,你只能通過呼叫pipe()函式返回一個可以改動的物件。在Rx中,這是一個不同的型別叫做Subject
如果讀/寫這樣的概念聽起來不太明白,可以類比為未來/承諾(Future
/Promise)。未來只是一個只讀的佔位符(A Future is a read-only placeholder),就像Signal/SignalProducer和Observable。另外一方面,對於未來的承諾確可以手動自由的實現,就像pipe()和Subject。

Schedulers

這個部分兩個框架都很類似,同樣的概念。但是RAC是連續的,序列,Rx可以支援併發。

Composition

合成(Composition)是響應式程式設計的主要特點。合成成流都是兩個框架的核心,在Rx中也稱作sequences
在Rx中所有observable的實體的型別都是ObservableType,所以我們可以輕鬆的用同一個操作符將Subject和Observable的例項組合(compose)起來。
在RAC中,Signal和SignalProducer是兩種不同的物件。我們必須把SignalProducer轉換成Signal後才能compose由Signal例項產生的訊號。這兩個物件擁有各自的操作符。所以當你需要混合使用它們時,你必須考慮到某種操作符是否是兩者通用,這個時候你也不必關心冷/熱的處理了。

關於這個部分, Colin Eberhardt 很好的總結了:
現在signal的API主要關注在處理‘next’上,讓你可以改變值,skip, delay, combine並且在不同的執行緒裡觀察值。signal producer的API主要處理訊號的生命週期事件(completed, error),和一些這樣的操作:then, flatMap, takeUntil 和catch。

其他

在RAC中還有Action Property的概念。前者是一種處理副作用的型別,主要和使用者互動相關。後者用於觀察當執行了一個任務後值改變了的情況。在Rx中Action也會轉變(translate)成一個 Observable型別。這在RxCocoa中有很好的體現,一個整合了Rx基本元素後用於iOS和Mac平臺。RAC中的 Property可以對應於Rx中的 Variable或者 BehaviourSubject
明白 Property/ Variable是我們連線必然世界和宣告本質的響應式程式設計的的橋樑(bridge the imperative world to the declarative nature of Reactive Programming),所以當我們處理一些第三方庫或者iOS/Mac中的核心功能(functionalities)時, Property/ Variable有時是基礎。

結論

RAC和Rx可以說是兩種完全不同的物種,前者在Cocoa裡有著長的歷史和大量的參與者,後者很年輕,但是依靠著已經在其他語言裡像java、js或者C#被驗證過的有效理念。關於選誰比較好還是要考慮到自己的情況。RAC認為把觀察的物件區分為熱/冷是非常有必要的,並且這也是他們框架的一個核心特點。Rx則認為把這個統一為一種物件更好。再次說明,這影響的只是怎麼處理訂閱後的副作用。
RAC 3.0 在為了區分觀察時的熱/冷狀態還引入了意料之外的複雜度,比如中斷的概念,區分兩種物件間不同的操作,引入一些必要行為(imperative behaviour)比如start是開始產生訊號。對於一些人來說,這些東西很好甚至是一個殺手級功能,對於另外一些人而言會覺得這個並沒有必要甚至有些危險。另外需要記住的一點是RAC一直努力和Cocoa的慣例儘量保持一致,如果你是一個資深的Cocoa開發者,你在使用RAC應該會覺得比Rx更順手。
Rx中所有的物件都是observables。好的一件事是,是Reactive Extensions中的一員。從RxJS, RxJava 或者 Rx.Net中遷移過來是一件非常簡單的事,所有概念都是一樣的。這也讓解決問題時會很有趣,因為你現在面臨的問題,可能在RxJava中已經有人寫過解決方案,你可以直接拿過來按照當前平臺實現就可以。
這兩個應該選哪一個關鍵看使用習慣,從一個客觀的角度來說無法分辨出誰更好。最好的方式是開啟Xcode,都試著使用這兩個框架,看看那個用起來比較順手。他們都是相同程式設計理念的實現,嘗試達到同一個目的:使開發軟體變得簡單(simplifying software development)。

相關文章