轉自: http://blog.csdn.net/Hello_Hwc/article/details/51859330
前言
RxSwift是Swift函數響應式編程的一個開源庫,由Github的ReactiveX組織開發,維護。
RxSwift的目的是讓讓數據/事件流和異步任務能夠更方便的序列化處理,能夠使用Swift進行響應式編程
目前,RxSwift在Github上收到了5000+Star,600+fork。
本文的目的
- 介紹RxSwift的核心思想
- 講解RxSwift的基礎使用
- 介紹RxSwift的優點
如果你有時間,建議先讀讀RxSwift的文檔,這會給你一個最基本的認識
本文不會講解函數式編程,也不會講解函數響應式編程的概念,計劃後面單獨出一篇博客來講解Swift與函數式編程。
本文來自於官方文檔的翻譯,官方example代碼的閱讀,以及自己的理解
RxSwift和ReativeCocoa
老一點的iOS開發者應該對ReativeCocoa有一些瞭解,iOS響應式編程的鼻祖。就個人來看
- ReativeCocoa更適合OC,缺點語法複雜,概念繁多,參考資料少(尤其RAC4),不易理解
- RxSwift對Swift的兼容很好,利用了很多的Swift特性,語法簡單,概念清楚
So,個人是非常推薦RxSwift的
Observables/Sequences
先複習下SequenceType。這是Swift中的一個協議,比如Swift中的Array就遵循這個協議,通過這個協議,你可以這樣的去操作一個Array
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
也就是說,把Array作爲一個序列,然後依次對這個序列進行過濾,映射等操作,也可以通過indexGenerator來一個個的獲取序列中的數據。
RxSwift的核心思想和這個類似。
RxSwift的核心是想是 Observable<Element> sequence
,Observable表示可監聽或者可觀察,也就是說RxSwift的核心思想是可監聽的序列。並且,Observable
sequence可以接受異步信號,也就是說,信號是可以異步給監聽者的
- Observable(ObservableType) 和 SequenceType類似
- ObservableType.subscribe 和 SequenceType.generate類似
- 由於RxSwift支持異步獲得信號,所以用
ObservableType.subscribe
,這和indexGenerator.next()
類似
本文把RxSwift中的序列的每一個Element成爲信號,因爲異步的Element是與時間相關的,稱作信號更好理解一點
RxSwift中,ObservableType.subscribe
的回調(新的信號到來)一共有三種
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
取消監聽
Observable分爲兩種
- 在有限的時間內會自動結束(Completed/Error),比如一個網絡請求當作一個序列,當網絡請求完成的時候,Observable自動結束,資源會被釋放
- 信號不會自己結束,最簡單的比如一個Timer,每隔一段時間發送一個新的信號過來,這時候需要手動取消監聽,來釋放相應的資源,又比如一個label.rac_text是一個Obserable,通常需要這樣調用
addDisposableTo(disposeBag)
來讓其在deinit,也就是所有者要釋放的時候,自動取消監聽。
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
當然,除了手動釋放,RxSwift提供了一些操作符,比如 takeUntil
來根據條件取消
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
信號處理的順序
Observable有個隱式的約定,那就是在一個信號處理完成之前,不會發送下一個信號,不管發送信號的線程是併發的or串行的。
比如
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
只會出現
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
不會出現
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
第一個例子
我們監聽textfield的文字變化,然後,Log出text,當button點擊的時候,取消這次監聽
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
RxSwift用extensiton的方式,爲UITextfield,UIlabel等控件添加了很多可監聽的屬性,這裏的textfield.rx_text就是一個
效果:隨着文字輸入,實時Log出textfield的文字,當點擊button之後,再輸入,則不會Log
操作符(Operators)
在上文的第一個例子裏面,你看到了監聽信號,並且log出值。事實上,這樣直接處理信號的時候是很少的,很多時候,我們需要對信號進行映射,過濾,這時候我們就要用到操作符了。在這個文檔裏,你可以找到所有的操作符。
關於操作符效果,你可以參見http://rxmarbles.com/的可視化效果,這會給你一個更好的理解
例子二,map,filter,combineLatest
- map 對信號(Element)進行映射處理。比如輸入是String,影射到Bool
- filter 對信號(Element)進行過濾處理。返回信號,和輸入的信號是同一種類型
- combineLatest 對兩種信號的值進行結合。可以返回不同種類的信號。
例如
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
對於,每一個fistTextfield的信號,在字符串開始處增加”first”;對secondTextfield的信號進行過濾,當長度大於3的時候,纔會繼續傳遞。對兩個信號進行結合,取truple類型,然後打印出來。
所以,當我在fistTextfield中,輸入1234,然後secondTextfield中依次輸入abcdefg的時候
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
例子三,創建一個Observable
Observerable可以用來處理任務,並且異步返回Event信號(Next,Error,Completion)
比如,這樣一個方法
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
這樣調用
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
然後,Log如下
- 1
- 2
- 1
- 2
可以看到,創建一個Observable相當容易,調用Observable.create,在必要的時候發送onNext,onError,onCompleted信號。然後返回一個Disposable用來取消信號
throttle/retry/distinctUntilChanged/flatMapLatest
- throttle 忽略上一個信號的一段時間的變化,也就是說一段時間沒有新的信號輸入,纔會向下發送
- distinctUntilChanged 直到信號改變了再發送
- retry 如果失敗,重新嘗試的次數
- flatMapLatest 僅僅執行最新的信號,當有新的信號來的時候,取消上一次未執行完的整個序列
最直接的例子就是搜索,通常我們想要
- 用戶用一段時間沒有輸入的時候,在進進行網絡請求,不然網絡請求太頻繁,對客戶端和服務器都是負擔
- 當新的請求來的時候,如果上一個未完成,則取消上一個
- 如果網絡失敗,能重新請求幾次就更好了
這時候,用RxSwift你得代碼會變的非常簡單
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
這裏簡單講解下作用
throttle(0.3, scheduler: MainScheduler.instance)
保證用戶沒有輸入0.3秒後再進行下一步distinctUntilChanged()
假如0.3秒之前輸入是ab,0.3秒後還是ab,則不會進行下一步,只有改變了纔會進行下一步flatMapLatest
保證只搜索最新的,如果之前的沒有完成,會被自動取消doSearchAPI(query).retry(3)
保證,如果發生錯誤,自動重試3次
Schedulers
Schedulers 抽象化了線程,線程池,GCD中操作隊列,Runloop等概念。可以理解爲,Schedulers就是一個執行任務的線程。
有一點要注意:默認一個Observerable在其創建的線程上執行
與Schedulers相關的操作符有兩個
- observeOn(scheduler) 在一個scheduler上執行任務,使用場景較多
- subscribeOn(scheduler) 在一個scheduler進行監聽
比如
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
默認一個subscribeNext或者subscribe在其調用的線程上執行
Serial/Concurrent Schedulers 串行或並行
和GCD的隊列很相似,並行Schedulers的任務可以併發之行,串行Schedulers只能依次之行。不過RxSwift有內部機制,保證上文提到的信號處理的順序
RxSwift內置的Scheduler
通常,使用內置的Scheduler足矣。
CurrentThreadScheduler
(串行) 當前線程Scheduler,默認使用的MainScheduler
(串行) 主線程SerialDispatchQueueScheduler
封裝了GCD的串行隊列ConcurrentDispatchQueueScheduler
封裝了GCD的並行隊列,這個在有任務要在後臺執行的時候很有用OperationQueueScheduler
封裝了NSOperationQueue
例子四,在後臺Scheduler之行任務,然後在主線程上更新UI
Variable
Variable表示一個可監聽的數據結構。使用Variable,你可以監聽數據的變化,也可以把其他值綁定到它身上。
當Variable被釋放的時候,它會向監聽者發送onCompleted
例子五,Variable進行監聽
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
數據綁定
數據綁定是開發的時候很常見的,比如根據文本的輸入動態調整textfield的背景色,動態調整按鈕的enable。亦或者根據textfield的輸入變化,動態的去反饋到model層。如果你聽過MVVM,那你肯定知道,MVVM的難點就是ViewModel與View的數據綁定問題。
不過,使用RxSwift,數據綁定變的十分容易,你甚至可以把數據綁定到tableview和collectionView上去。
例子六,bindTo
很簡單,隨着Switch的開關,view進行顯示/隱藏
只需要一行代碼
- 1
- 1
例子七,根據輸入,進行View狀態綁定
我們想要實現這樣的狀態
- 用戶名至少6位,小於6位,則背景色是灰色,合法則透明
- 密碼至少位8位,小於8位,則背景色是灰色,合法則透明
- 當用戶名和密碼都合法的時候,註冊按鈕enable,並且背景色變紅
信號的處理方式如下,
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
共享監聽Sharing subscription-shareReplay
這個是很常用的,比如一個Obserable用做網絡請求,通常,當你這樣調用的時候,會創建兩個序列,也就是會進行兩次網絡請求,這是不需要的
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
爲了共享一個序列,你只需要這這樣調用
- 1
- 1
就只會進行一次網絡請求,兩個subscription共享結果,也就是shareReplay的意思
自定義可綁定屬性
上文,textfield和button的狀態綁定是手動的,這無疑是不方便的。RxSwift爲我們提供了一種方式,來自定義可綁定屬性
創建兩個exetnsion
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
然後,上文的代碼,就可以簡化成三行了,So easy
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
Driver
Driver是RxSwift精心製作的,專門提供給UI層的一個接口。
利用Driver你可以
- 利用CoreData的模型來驅動UI
- 利用UI的狀態來綁定其他UI的狀態
Driver能夠保證,在主線程上監聽,因爲UIKit不是需要在主線程上操作
Tips:
RxSwift中做數據綁定有三種
- 利用BindTo方法
- 利用Driver(強烈建議使用這個,)
- 利用KVO來手動綁定(很少用到)
回到Driver上來,上文提到了,對於搜索,我們可以這麼做,
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
那麼,這有什麼缺陷呢?
- 假如searchWithText失敗了,那麼整個序列就斷掉了,後面的綁定不會有任何作用
- 假如searchWithText是在後臺線程執行的,那麼後續綁定是在後臺線程上進行的,會崩潰
- 綁定了兩次,意味着會執行兩次
於是,我們需要進行額外的操作,來避免上述缺陷。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
利用Driver我們可以將上述過程簡化
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
任何滿足以下三個條件的Observer序列都可以轉換爲Driver
- 不會因爲錯誤就序列斷掉(比如,有錯誤,但是沒有調用onError來發送錯誤)
- 在主線程傻姑娘監聽
- 共享 side effects
對於,使用者只需要調用asDriver(onErrorJustReturn: [])
就能保證上述三點都實現了
KVO
通常的KVO,你需要在這個函數裏來處理
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
而使用RxSwift,KVO變成了這樣
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
或者這樣
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
二者的區別是
在rx_observe可以使用地方都可以使用rx_observeWeakly。rx_observeWeakly的執行效率要低一點,因爲要處理對象的dealloc關係。除此之外,rx_observeWeakly還可以用在weak屬性上。
在使用view.rx_observe的時候,有幾點要注意
由於KVO是建立在NSObject子類的基礎上的,你可以通過如下方法,來讓Structs支持KVO
Notification
使用RxSwift,Notification變的十分簡潔
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
DEBUG
調試編譯問題
Swift是一個強類型語言,能夠在絕大部分場景自動推斷出變量的類型。比如
- 1
- 1
但是,有些時候RxSwift的序列處理會報編譯錯誤
比如
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
如果,Swift沒辦法推斷出類型,那麼最直接的方式,就是顯式的告訴Swift類型是什麼
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
調試
使用Debug操作符,會log所有的數據流
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
你可以用自定義操作符的方式,來Log
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
調試內存泄漏問題
RxSwift通過RxSwift.resourceCount
記錄資源分配情況,所以通常的調試方式如下
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 1
- 2
- 3
- 4
- 5
- 6
- 7
然後
- 進入相關界面,進行正常操作
- 退出界面
- 觀察RxSwift.resourceCount
- 在進入同一個界面,退出
- 觀察RxSwift.resourceCount
使用心得
- 時刻牢記,使用RxSwift,儘量把所有的任務(可以理解爲方法)抽象成Obserable(序列)和Obserable創建者,監聽者
- 能用數據綁定的(bindTo和Driver)的就不要手動綁定
- 一定要熟練RxSwift提供的操作符,要會自定義操作符
RxSwift的優點
- Composable 可組合,在設計模式中有一種模式叫做組合模式,你可以方便的用不同的組合實現不同的類
- Reusable 代碼可重用,原因很簡單,對應RxSwift,就是一堆Obserable
- Declarative 響應式的,因爲狀態不可變,只有數據變化
- Understandable and concise 簡潔,容易理解。
- Stable 穩定,因爲RxSwift寫出的代碼,單元測試時分方便
- Less stateful “無”狀態性,因爲對於響應式編程,你的應用程序就是一堆數據流
- Without leaks 沒有泄漏,因爲資源管理非常簡單