前言
看完本系列前面幾篇之后,估計(jì)大家也還是有點(diǎn)懵逼,本系列前八篇也都是參考RxSwift
官方文檔和一些概念做的解讀。上幾篇文章概念性的東西有點(diǎn)多,一時(shí)也是很難全部記住,大家腦子里面知道有這么個(gè)概念就行,用的時(shí)候,再來(lái)查閱一番,慢慢就掌握了。
本篇主要來(lái)深入了解一些RxSwift
實(shí)戰(zhàn)中用到的一些重要知識(shí)點(diǎn),這里面有很多自己的理解,所以不免會(huì)有一些錯(cuò)誤的地方,還請(qǐng)大家多多交流,如有發(fā)現(xiàn)錯(cuò)誤的地方,歡迎評(píng)論。
概念
Rx
系列的核心就是Observable Sequence
這個(gè)相信大家心中已經(jīng)有所了解了,這里不再啰嗦了,建議大家看看前面幾篇文章去了解一下。接下來(lái)介紹一些容易混淆和難以理解的概念。
Observable 和 Observer
相信大家看前面幾篇文章的時(shí)候,大量出現(xiàn)這兩個(gè)東西,為了理解這兩個(gè)東西,我們先來(lái)簡(jiǎn)單介紹下觀察者模式吧。
比如一個(gè)寶寶在睡覺(jué),爸爸媽媽不可能時(shí)時(shí)刻刻待在那看著吧?那樣子太累
了。他們?cè)撟錾蹲錾?,只要聽到寶寶哭聲的時(shí)候,他們給寶寶喂奶就行了。這就是一個(gè)簡(jiǎn)單的觀察者模式。寶寶是被觀察者,爸爸媽媽是觀察者也稱作訂閱者,只要被觀察者發(fā)出了某一個(gè)事件,比如寶寶哭聲,叫聲都是一個(gè)事件,訂閱者就會(huì)做出相應(yīng)地響應(yīng)。
理解了觀察者模式這兩個(gè)概念就很好理解了,Observable
就是可被觀察的,也就是我們說(shuō)的寶寶,他也是事件源。而Observer
就是我們的觀察者,也就是當(dāng)收到事件的時(shí)候去做某些處理的爸爸媽媽。觀察者需要去訂閱(subscribe
)被觀察者,才能收到Observable
的事件通知消息。
subscribe 和 subscribe(onNext:)
subscribe
是訂閱sequence
發(fā)出的事件,比如next
事件,error
事件等。而subscribe(onNext:)
是監(jiān)聽sequence
發(fā)出的next
事件中的element
進(jìn)行處理,他會(huì)忽略error
和completed
事件。相對(duì)應(yīng)的還有subscribe(onError:)
和 subscribe(onCompleted:)
。
Dispose 和 DisposeBag
當(dāng)監(jiān)聽一個(gè)sequence
的時(shí)候,有消息事件來(lái)了,我們做某些事情。但是這個(gè)sequence
不再發(fā)送消息事件了,那么我們的監(jiān)聽也就沒(méi)有什么存在的價(jià)值了,所以我們需要釋放我們這些監(jiān)聽資源,其實(shí)也就是內(nèi)存資源釋放。
釋放某一個(gè)監(jiān)聽的時(shí)候,我們有兩種方式處理:
我們可以手動(dòng)調(diào)用釋放方式,但是我們一般不適用這種方式。
// 關(guān)于scheduler,我們會(huì)在下面講到let subscription = Observable<Int>.interval(0.3, scheduler: SerialDispatchQueueScheduler.init(internalSerialQueueName: "scott")) .observeOn(MainScheduler.instance) //observeOn也會(huì)在下面講到 .subscribe { event in print(event) } Thread.sleep(forTimeInterval: 2.0) subscription.dispose()
打印結(jié)果:
next(0)next(1)next(2)next(3)next(4)next(5)
比如上面這個(gè)例子,我們創(chuàng)建了一個(gè)subscription
監(jiān)聽,在兩秒以后我們不需要了,手動(dòng)調(diào)用dispose()
方法,就能釋放監(jiān)聽資源,不再打印信息。上面的subscription
不論是在哪個(gè)線程中監(jiān)聽,就算在主線程中調(diào)用的dispose()
方法一樣會(huì)銷毀資源。
除了上述手動(dòng)釋放資源外,還有一種自動(dòng)方式,推薦大家使用這種方式,這種方式就像iOS
中的ARC
,會(huì)在適當(dāng)?shù)臅r(shí)候銷毀觀察者,自動(dòng)釋放資源。
let disposeBag = DisposeBag() Observable<Int>.empty() .subscribe { event in print(event) } .addDisposableTo(disposeBag)
如上個(gè)例子,我們創(chuàng)建一個(gè)disposeBag
來(lái)盛放我們需要管理的資源,然后把新建的監(jiān)聽都放進(jìn)去,會(huì)在適當(dāng)?shù)臅r(shí)候銷毀這些資源。如果你需要立即釋放資源只需要新建一個(gè)DisposeBag()
,那么上一個(gè)DisposeBag
就會(huì)被銷毀。
observeOn() 和 subscribeOn()
這兩個(gè)東西剛開始看的時(shí)候也是一臉懵逼,就知道最好多用observeOn()
,但是不知道為什么,下面我們就來(lái)揭開它們的面紗看下它們的真面目吧。
區(qū)別其實(shí)我感覺(jué)就一句話,subscribeOn()
設(shè)置起點(diǎn)在哪個(gè)線程,observeOn()
設(shè)置了后續(xù)工作在哪個(gè)線程。例如:
someObservable .doOneThing() // 1 .observeOn(MainRouteScheduler.instance) // 2 .subscribeOn(OtherScheduler.instance) // 3 .subscribeNext { // 4 ...... } .addDisposableTo(disposeBag)
所有動(dòng)作都發(fā)生在當(dāng)前的默認(rèn)線程
observeOn()
轉(zhuǎn)換線程到主線程,下面所有的操作都在主線程subscribeOn()
規(guī)定動(dòng)作一開始不是發(fā)生在默認(rèn)線程,而是在OtherScheduler
了。如果我們之前沒(méi)有調(diào)用
observeOn()
,那么這邊會(huì)在OtherScheduler
發(fā)生,但是我們前面調(diào)用了observeOn()
,所以這個(gè)動(dòng)作會(huì)在主線程中調(diào)用。
總結(jié)一下:
subscribeOn()
只是影響事件鏈開始默認(rèn)的線程,而observeOn()
規(guī)定了下一步動(dòng)作發(fā)生在哪個(gè)線程中。
shareReplay
看官方項(xiàng)目里面的Demo
時(shí),我也很疑惑,為什么很多的sequence
后面都有shareReplay(1)
呢?想的昏頭漲腦。
這里我就給大家講解一下我的理解吧。先看一個(gè)例子:
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)
打印結(jié)果:
??Even:next(())Even:completed??Even:next(())Even:completed
大家發(fā)現(xiàn)沒(méi)有,map()
函數(shù)執(zhí)行了兩次,但是有些時(shí)候,我們并不想讓map()
函數(shù)里面的東西執(zhí)行兩次,比如map()
函數(shù)里面執(zhí)行的是網(wǎng)絡(luò)請(qǐng)求,我只需要執(zhí)行一次請(qǐng)求,然后把結(jié)果提供給大家使用就行了,多余的請(qǐng)求會(huì)增加負(fù)擔(dān)。所以這時(shí)候就需要使用shareReplay(1)
了。這里面的數(shù)字一般是1
,只執(zhí)行一次。(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)
打印結(jié)果:
??Even:next(())Even:completedEven:next(())Even:completed
自定義operator
自定義操作符很簡(jiǎn)單,官方推薦盡量使用標(biāo)準(zhǔn)的操作符,但是你也可以定義自己的操作符,文檔上說(shuō)有兩種方法,這里介紹一下常用的一種方法吧。
例如我們自定義一個(gè)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 } } }
參數(shù)是一個(gè)閉包,其中閉包參數(shù)是E類型返回值是R類型,map函數(shù)的返回值是一個(gè)Observable
類型。
Driver
這又是啥東東? 講解Driver
之前我們現(xiàn)在看看下面的??:
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)
首先創(chuàng)建一個(gè)可監(jiān)聽序列
results
,其中flatMapLatest
下面會(huì)講;然后將
results
綁定到resultCount.rx.text
上;將
results
綁定到resultsTableView
上.
上面程序會(huì)出現(xiàn)下面幾個(gè)異常:
如果
fetchAutoCompleteItems
出錯(cuò),那么它綁定的UI將不再收到任何事件消息;如果
fetchAutoCompleteItems
發(fā)生在后臺(tái)線程,那么它綁定的事件也將在后臺(tái)線程執(zhí)行,這樣更新UI會(huì)造成crash
;有兩次綁定,
fetchAutoCompleteItems
就會(huì)執(zhí)行兩次
當(dāng)然針對(duì)以上問(wèn)題,我們也有解決方案,針對(duì)第三點(diǎn),我們可以使用神器shareReplay(1)
保證只執(zhí)行一次,可以使用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
來(lái)解決:
let results = query.rx.text.asDriver() .throttle(0.3, scheduler: MainScheduler.instance) .flatMapLatest { query in fetchAutoCompleteItems(query) .asDriver(onErrorJustReturn: []) //當(dāng)遇見(jiàn)錯(cuò)誤需要返回什么 } //不需要添加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
有以下特點(diǎn):
Driver
序列不允許發(fā)出error
,Driver
序列的監(jiān)聽只會(huì)在主線程中。
所以Driver
是專為UI綁定量身打造的東西。
以下情況你可以使用Driver
替換BindTo
:
不能發(fā)出error;
在主線程中監(jiān)聽;
共享事件流;
map 和 flatMap 何時(shí)使用
看了前面《RxSwift 系列(四) -- Transforming Operators》,我想大家對(duì)于何時(shí)使用map
和flatMap
也還是有疑惑。
我們來(lái)看看map
函數(shù)和flatMap
函數(shù)的定義:map
函數(shù),接收一個(gè)R類型的序列,返回一個(gè)R類型的序列,還是原來(lái)的序列。
public func map<R>(_ transform: @escaping (Self.E) throws -> R) -> RxSwift.Observable<R>
flatMap
函數(shù),接收一個(gè)O類型的序列,返回一個(gè)O.E類型的序列,也就是有原來(lái)序列里元素組成的新序列。
public func flatMap<O: ObservableConvertibleType>(_ selector: @escaping (E) throws -> O) -> Observable<O.E>
其實(shí)這里的map
和flatMap
在swift
中的作用是一樣的。map
函數(shù)可以對(duì)原有序列里面的事件元素進(jìn)行改造,返回的還是原來(lái)的序列。而flatMap
對(duì)原有序列中的元素進(jìn)行改造和處理,每一個(gè)元素返回一個(gè)新的sequence
,然后把每一個(gè)元素對(duì)應(yīng)的sequence
合并為一個(gè)新的sequence
序列。
看下面例子:
let disposeBag = DisposeBag() let observable = Observable.of("1","2","3","4","5").map{$0 + "scott"} observable.subscribe(onNext: {print($0)}).disposed(by: disposeBag)
打印結(jié)果:
1scott 2scott 3scott 4scott 5scott
我們使用map
對(duì)序列中每一個(gè)元素進(jìn)行了處理,返回的是一個(gè)元素,而使用flatMap
需要返回的序列。那么使用map
也返回一個(gè)序列看看。
let test = Observable.of("1", "2", "3", "4", "5") .map { Observable.just($0) } test.subscribe(onNext: { print($0) }) .addDisposableTo(disposeBag)
運(yùn)行結(jié)果:
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>
看到結(jié)果會(huì)打印出每一個(gè)序列,下面我們使用merge()
方法將這幾個(gè)序列進(jìn)行合并:
let test = Observable.of("1", "2", "3", "4", "5") .map { Observable.just($0) }.merge() test.subscribe(onNext: { print($0) }) .addDisposableTo(disposeBag)
運(yùn)行結(jié)果:
1 2 3 4 5
合并為一個(gè)新序列后我們就可以正常打印元素了。下面看看使用faltMap()
函數(shù)干這件事:
let test = Observable.of("1", "2", "3", "4", "5") .flatMap { Observable.just($0) } test.subscribe(onNext: { print($0) }) .addDisposableTo(disposeBag)
運(yùn)行結(jié)果:
1 2 3 4 5
看下對(duì)比是不是一樣,這樣子對(duì)比就清晰了吧。
map函數(shù)只能返回原來(lái)的那一個(gè)序列,里面的參數(shù)的返回值被當(dāng)做原來(lái)序列中所對(duì)應(yīng)的元素。
flatMap函數(shù)返回的是一個(gè)新的序列,將原來(lái)元素進(jìn)行了處理,返回這些處理后的元素組成的新序列
map函數(shù) + 合并函數(shù) = flatMap函數(shù)
flatMap
函數(shù)在實(shí)際應(yīng)用中有很多地方需要用到,比如網(wǎng)絡(luò)請(qǐng)求,網(wǎng)絡(luò)請(qǐng)求可能會(huì)發(fā)生錯(cuò)誤,我們需要對(duì)這個(gè)請(qǐng)求過(guò)程進(jìn)行監(jiān)聽,然后處理錯(cuò)誤。只要繼續(xù)他返回的是一個(gè)新的序列。
UIBindingObserver
UIBindingObserver
這個(gè)東西很有用的,創(chuàng)建我們自己的監(jiān)聽者,有時(shí)候RxCocoa
(RxSwift
中對(duì)UIKit
的一個(gè)擴(kuò)展庫(kù))給的擴(kuò)展不夠我們使用,比如一個(gè)UITextField
有個(gè)isEnabled
屬性,我想把這個(gè)isEnabled
變?yōu)橐粋€(gè)observer
,我們可以這樣做:
extension Reactive where Base: UITextField { var inputEnabled: UIBindingObserver<Base, Result> { return UIBindingObserver(UIElement: base) { textFiled, result in textFiled.isEnabled = result.isValid } } }
UIBindingObserver
是一個(gè)類,他的初始化方法中,有兩個(gè)參數(shù),第一個(gè)參數(shù)是一個(gè)元素本身,第一個(gè)參數(shù)是一個(gè)閉包,閉包參數(shù)是元素本身,還有他的一個(gè)屬性。
public init(UIElement: UIElementType, binding: @escaping (UIElementType, Value) -> Swift.Void)
自定義了一個(gè)inputEnabled
關(guān)聯(lián)了UITextField
的isEnabled
屬性。
致謝
本系列文章理論性的東西就算是講述完了,如果你發(fā)現(xiàn)有錯(cuò)誤,歡迎交流,共同進(jìn)步,謝謝。接下來(lái)準(zhǔn)備寫點(diǎn)實(shí)戰(zhàn)性的,大家準(zhǔn)備好!
http://www.cnblogs.com/scott-mr/p/7234769.html