看完本系列前面幾篇以後,估計你們也仍是有點懵逼,本系列前八篇也都是參考RxSwift
官方文檔和一些概念作的解讀。上幾篇文章概念性的東西有點多,一時也是很難所有記住,你們腦子裏面知道有這麼個概念就行,用的時候,再來查閱一番,慢慢就掌握了。html
本篇主要來深刻了解一些RxSwift
實戰中用到的一些重要知識點,這裏面有不少本身的理解,因此難免會有一些錯誤的地方,還請你們多多交流,若有發現錯誤的地方,歡迎評論。swift
Rx
系列的核心就是Observable Sequence
這個相信你們心中已經有所瞭解了,這裏再也不囉嗦了,建議你們看看前面幾篇文章去了解一下。接下來介紹一些容易混淆和難以理解的概念。api
相信你們看前面幾篇文章的時候,大量出現這兩個東西,爲了理解這兩個東西,咱們先來簡單介紹下觀察者模式吧。網絡
好比一個寶寶在睡覺,爸爸媽媽不可能時時刻刻待在那看着吧?那樣子太累
了。他們該作啥作啥,只要聽到寶寶哭聲的時候,他們給寶寶餵奶就好了。這就是一個簡單的觀察者模式。寶寶是被觀察者,爸爸媽媽是觀察者也稱做訂閱者,只要被觀察者發出了某一個事件,好比寶寶哭聲,叫聲都是一個事件,訂閱者就會作出相應地響應。閉包
理解了觀察者模式這兩個概念就很好理解了,Observable
就是可被觀察的,也就是咱們說的寶寶,他也是事件源。而Observer
就是咱們的觀察者,也就是當收到事件的時候去作某些處理的爸爸媽媽。觀察者須要去訂閱(subscribe
)被觀察者,才能收到Observable
的事件通知消息。函數
subscribe
是訂閱sequence
發出的事件,好比next
事件,error
事件等。而subscribe(onNext:)
是監聽sequence
發出的next
事件中的element
進行處理,他會忽略error
和completed
事件。相對應的還有subscribe(onError:)
和 subscribe(onCompleted:)
。fetch
當監聽一個sequence
的時候,有消息事件來了,咱們作某些事情。可是這個sequence
再也不發送消息事件了,那麼咱們的監聽也就沒有什麼存在的價值了,因此咱們須要釋放咱們這些監聽資源,其實也就是內存資源釋放。
釋放某一個監聽的時候,咱們有兩種方式處理:spa
咱們能夠手動調用釋放方式,可是咱們通常不適用這種方式。線程
// 關於scheduler,咱們會在下面講到 let subscription = Observable<Int>.interval(0.3, scheduler: SerialDispatchQueueScheduler.init(internalSerialQueueName: "scott")) .observeOn(MainScheduler.instance) //observeOn也會在下面講到 .subscribe { event in print(event) } Thread.sleep(forTimeInterval: 2.0) subscription.dispose()
打印結果:code
next(0) next(1) next(2) next(3) next(4) next(5)
好比上面這個例子,咱們建立了一個subscription
監聽,在兩秒之後咱們不須要了,手動調用dispose()
方法,就能釋放監聽資源,再也不打印信息。上面的subscription
不管是在哪一個線程中監聽,就算在主線程中調用的dispose()
方法同樣會銷燬資源。
除了上述手動釋放資源外,還有一種自動方式,推薦你們使用這種方式,這種方式就像iOS
中的ARC
,會在適當的時候銷燬觀察者,自動釋放資源。
let disposeBag = DisposeBag() Observable<Int>.empty() .subscribe { event in print(event) } .addDisposableTo(disposeBag)
如上個例子,咱們建立一個disposeBag
來盛放咱們須要管理的資源,而後把新建的監聽都放進去,會在適當的時候銷燬這些資源。若是你須要當即釋放資源只須要新建一個DisposeBag()
,那麼上一個DisposeBag
就會被銷燬。
這兩個東西剛開始看的時候也是一臉懵逼,就知道最好多用observeOn()
,可是不知道爲何,下面咱們就來揭開它們的面紗看下它們的真面目吧。
區別其實我感受就一句話,subscribeOn()
設置起點在哪一個線程,observeOn()
設置了後續工做在哪一個線程。例如:
someObservable .doOneThing() // 1 .observeOn(MainRouteScheduler.instance) // 2 .subscribeOn(OtherScheduler.instance) // 3 .subscribeNext { // 4 ...... } .addDisposableTo(disposeBag)
observeOn()
轉換線程到主線程,下面全部的操做都在主線程subscribeOn()
規定動做一開始不是發生在默認線程,而是在OtherScheduler
了。observeOn()
,那麼這邊會在OtherScheduler
發生,可是咱們前面調用了observeOn()
,因此這個動做會在主線程中調用。總結一下:
subscribeOn()
只是影響事件鏈開始默認的線程,而observeOn()
規定了下一步動做發生在哪一個線程中。
看官方項目裏面的Demo
時,我也很疑惑,爲何不少的sequence
後面都有shareReplay(1)
呢?想的昏頭漲腦。
這裏我就給你們講解一下個人理解吧。先看一個例子:
let disposeBag = DisposeBag() let observable = Observable.just("🤣").map{print($0)} observable.subscribe{print("Even:\($0)")}.disposed(by: disposeBag) observable.subscribe{print("Even:\($0)")}.disposed(by: disposeBag)
打印結果:
🤣 Even:next(()) Even:completed 🤣 Even:next(()) Even:completed
你們發現沒有,map()
函數執行了兩次,可是有些時候,咱們並不想讓map()
函數裏面的東西執行兩次,好比map()
函數裏面執行的是網絡請求,我只須要執行一次請求,而後把結果提供給你們使用就好了,多餘的請求會增長負擔。因此這時候就須要使用shareReplay(1)
了。這裏面的數字通常是1
,只執行一次。(ps:我改爲 2,3 也只打印一次)
let disposeBag = DisposeBag() let observable = Observable.just("🤣").map{print($0)}.shareReplay(1) observable.subscribe{print("Even:\($0)")}.disposed(by: disposeBag) observable.subscribe{print("Even:\($0)")}.disposed(by: disposeBag)
打印結果:
🤣 Even:next(()) Even:completed Even:next(()) Even:completed
自定義操做符很簡單,官方推薦儘可能使用標準的操做符,可是你也能夠定義本身的操做符,文檔上說有兩種方法,這裏介紹一下經常使用的一種方法吧。
例如咱們自定義一個map
操做符:
extension ObserverType { func myMap<R>(transform: E -> R) -> Observable<R> { return Observable.create{ observer in let subscription = self.subscribe {e in switch e{ case .next(let value): let result = transform(value) observer.on(.next(result)) case .error(let error): observer.on(.error(error)) case .completed: observer.on(.completed) } } return subscription } } }
參數是一個閉包,其中閉包參數是E類型返回值是R類型,map函數的返回值是一個Observable
類型。
這又是啥東東? 講解Driver
以前咱們如今看看下面的🌰:
let results = query.rx.text .throttle(0.3, scheduler: MainScheduler.instance) .flatMapLatest { query in fetchAutoCompleteItems(query) } results .map { "\($0.count)" } .bindTo(resultCount.rx.text) .addDisposableTo(disposeBag) results .bindTo(resultsTableView.rx.items(cellIdentifier: "Cell")) { (_, result, cell) in cell.textLabel?.text = "\(result)" } .addDisposableTo(disposeBag)
results
,其中flatMapLatest
下面會講;results
綁定到resultCount.rx.text
上;results
綁定到resultsTableView
上.上面程序會出現下面幾個異常:
fetchAutoCompleteItems
出錯,那麼它綁定的UI將再也不收到任何事件消息;fetchAutoCompleteItems
發生在後臺線程,那麼它綁定的事件也將在後臺線程執行,這樣更新UI會形成crash
;fetchAutoCompleteItems
就會執行兩次固然針對以上問題,咱們也有解決方案,針對第三點,咱們可使用神器shareReplay(1)
保證只執行一次,可使用observeOn()
保證後面全部操做在主線程完成。
let results = query.rx.text .throttle(0.3, scheduler: MainScheduler.instance) .flatMapLatest { query in fetchAutoCompleteItems(query) .observeOn(MainScheduler.instance) .catchErrorJustReturn([]) } .shareReplay(1) results .map { "\($0.count)" } .bindTo(resultCount.rx.text) .addDisposableTo(disposeBag) results .bindTo(resultTableView.rx.items(cellIdentifier: "Cell")) { (_, result, cell) in cell.textLabel?.text = "\(result)" } .addDisposableTo(disposeBag)
咱們也可使用Driver
來解決:
let results = query.rx.text.asDriver() .throttle(0.3, scheduler: MainScheduler.instance) .flatMapLatest { query in fetchAutoCompleteItems(query) .asDriver(onErrorJustReturn: []) //當碰見錯誤須要返回什麼 } //不須要添加shareReplay(1) results .map { "\($0.count)" } .drive(resultCount.rx.text) //和bingTo()功能同樣 .addDisposableTo(disposeBag) results .drive(resultTableView.rx.items(cellIdentifier: "Cell")) { (_, result, cell) in cell.textLabel?.text = "\(result)" } .addDisposableTo(disposeBag)
drive
方法只能在Driver
序列中使用,Driver
有如下特色:
Driver
序列不容許發出error
,Driver
序列的監聽只會在主線程中。因此Driver
是專爲UI綁定量身打造的東西。
如下狀況你可使用Driver
替換BindTo
:
看了前面《RxSwift 系列(四) -- Transforming Operators》,我想你們對於什麼時候使用map
和flatMap
也仍是有疑惑。
咱們來看看map
函數和flatMap
函數的定義:map
函數,接收一個R類型的序列,返回一個R類型的序列,仍是原來的序列。
public func map<R>(_ transform: @escaping (Self.E) throws -> R) -> RxSwift.Observable<R>
flatMap
函數,接收一個O類型的序列,返回一個O.E類型的序列,也就是有原來序列裏元素組成的新序列。
public func flatMap<O: ObservableConvertibleType>(_ selector: @escaping (E) throws -> O) -> Observable<O.E>
其實這裏的map
和flatMap
在swift
中的做用是同樣的。map
函數能夠對原有序列裏面的事件元素進行改造,返回的仍是原來的序列。而flatMap
對原有序列中的元素進行改造和處理,每個元素返回一個新的sequence
,而後把每個元素對應的sequence
合併爲一個新的sequence
序列。
看下面例子:
let disposeBag = DisposeBag() let observable = Observable.of("1","2","3","4","5").map{$0 + "scott"} observable.subscribe(onNext: {print($0)}).disposed(by: disposeBag)
打印結果:
1scott 2scott 3scott 4scott 5scott
咱們使用map
對序列中每個元素進行了處理,返回的是一個元素,而使用flatMap
須要返回的序列。那麼使用map
也返回一個序列看看。
let test = Observable.of("1", "2", "3", "4", "5") .map { Observable.just($0) } test.subscribe(onNext: { print($0) }) .addDisposableTo(disposeBag)
運行結果:
RxSwift.(Just in _BD9B9D4356C4038796FB16D0D54A9F8E)<Swift.String> RxSwift.(Just in _BD9B9D4356C4038796FB16D0D54A9F8E)<Swift.String> RxSwift.(Just in _BD9B9D4356C4038796FB16D0D54A9F8E)<Swift.String> RxSwift.(Just in _BD9B9D4356C4038796FB16D0D54A9F8E)<Swift.String> RxSwift.(Just in _BD9B9D4356C4038796FB16D0D54A9F8E)<Swift.String>
看到結果會打印出每個序列,下面咱們使用merge()
方法將這幾個序列進行合併:
let test = Observable.of("1", "2", "3", "4", "5") .map { Observable.just($0) }.merge() test.subscribe(onNext: { print($0) }) .addDisposableTo(disposeBag)
運行結果:
1 2 3 4 5
合併爲一個新序列後咱們就能夠正常打印元素了。下面看看使用faltMap()
函數幹這件事:
let test = Observable.of("1", "2", "3", "4", "5") .flatMap { Observable.just($0) } test.subscribe(onNext: { print($0) }) .addDisposableTo(disposeBag)
運行結果:
1 2 3 4 5
看下對比是否是同樣,這樣子對比就清晰了吧。
flatMap
函數在實際應用中有不少地方須要用到,好比網絡請求,網絡請求可能會發生錯誤,咱們須要對這個請求過程進行監聽,而後處理錯誤。只要繼續他返回的是一個新的序列。
UIBindingObserver
這個東西頗有用的,建立咱們本身的監聽者,有時候RxCocoa
(RxSwift
中對UIKit
的一個擴展庫)給的擴展不夠咱們使用,好比一個UITextField
有個isEnabled
屬性,我想把這個isEnabled
變爲一個observer
,咱們能夠這樣作:
extension Reactive where Base: UITextField { var inputEnabled: UIBindingObserver<Base, Result> { return UIBindingObserver(UIElement: base) { textFiled, result in textFiled.isEnabled = result.isValid } } }
UIBindingObserver
是一個類,他的初始化方法中,有兩個參數,第一個參數是一個元素自己,第一個參數是一個閉包,閉包參數是元素自己,還有他的一個屬性。
public init(UIElement: UIElementType, binding: @escaping (UIElementType, Value) -> Swift.Void)
自定義了一個inputEnabled
關聯了UITextField
的isEnabled
屬性。
本系列文章理論性的東西就算是講述完了,若是你發現有錯誤,歡迎交流,共同進步,謝謝。接下來準備寫點實戰性的,你們準備好!