map操作符將源Observable的每個元素,通過提供的方法轉(zhuǎn)換,然后返回含有轉(zhuǎn)換后元素的Observable
#案例1:
Observable.of(1,2,3)
.map{
$0 * 10
}
.subscribe(onNext:{
print($0)
})
.disposed(by: bag)
/// 打印結(jié)果:
/// 10
/// 20
/// 30
flatMap操作符會對源Observable的每一個元素應(yīng)用一個轉(zhuǎn)換方法,將他們轉(zhuǎn)換成Observable,然后將這些Observable的元素合并之后再發(fā)送出來,即將其降維成一個Observable序列
#案例2:
/// 如果Observable中的元素也是Observable
Observable.of(1,2,3)
.map { value in
return Observable.just(value * 10)
}
.subscribe(onNext:{
print($0)
})
.disposed(by: bag)
/// 打印結(jié)果:
RxSwift.(unknown context at $10dc3e738).Just<Swift.Int>
RxSwift.(unknown context at $10dc3e738).Just<Swift.Int>
RxSwift.(unknown context at $10dc3e738).Just<Swift.Int>
/// 通過flatMap降維,獲取內(nèi)部的元素值
Observable.of(1,2,3)
.map { value in
return Observable.just(value * 10)
}
.flatMap({
return $0
})
.subscribe(onNext:{
print($0)
})
.disposed(by: bag)
}
/// 打印結(jié)果:
/// 10
/// 20
/// 30
#案例3:
# 這種寫法也是可以的,為什么?
# 請看下方講解
Observable.of(1,2,3)
.flatMap { (value) -> Observable<Int> in
return Observable.just(value * 10)
}
.subscribe(onNext:{
print($0)
})
.disposed(by: bag)
/// 打印結(jié)果:
/// 10
/// 20
/// 30
/// 源碼解析:
/// 首先selector函數(shù)型參數(shù)返回的是一個可觀察對象,因?yàn)楦鶕?jù)Source.Element可知,
/// 閉包函數(shù)返回的是一個可觀察對象,如果此閉包函數(shù)返回的不是一個可觀察對象則會報錯
///
/// 第二:
/// selector閉包函數(shù)入?yún)elf.Element,可以是普通的數(shù)據(jù)類型,也可以是可觀察對象
/// 如果是普通數(shù)據(jù)類型,那么就必須要在閉包函數(shù)中將其包裝為可觀察對象
/// 如果其本身就是可觀察對象,那么可以直接返回,不需要再包裝
public func flatMap<Source>(_ selector: @escaping (Self.Element) throws -> Source)
-> RxSwift.Observable<Source.Element>
where Source : RxSwift.ObservableConvertibleType
flatMapLatest: 當(dāng)源序列有新的事件發(fā)生的時候,flatMapLatest會自動取消上一個是事件的訂閱,轉(zhuǎn)到新的事件的訂閱上面,而flatMap則會訂閱全部
#案例4:
# 這種情況,flatMapLatest 與 flatMap 沒什么不同,不能體現(xiàn)兩者的區(qū)別
Observable.of(1,2,3)
.flatMapLatest { (value) -> Observable<Int> in
/// Observable.just 是一次性的可觀察序列,發(fā)生完Event后,就直接Complete結(jié)束
/// 不能再發(fā)送第二個次的Event
/// 所以這不能很好的體現(xiàn)flatMapLatest的主要功能:取消上一次的事件訂閱事件
return Observable.just(value * 10)
}
/// subscribe訂閱的不是Observable.of(1,2,3)創(chuàng)建的可觀察序列
/// 而是flatMap轉(zhuǎn)變后的可觀察序列
.subscribe(onNext:{
print($0)
})
.disposed(by: bag)
/// 打印結(jié)果:
/// 10
/// 20
/// 30
flatMapLatest 與 flatMap 使用區(qū)別案例
struct Player {
var score : BehaviorRelay<Int>
}
flatMap的實(shí)現(xiàn)
let aPlayer = Player(score: BehaviorRelay(value: 1))
let bPlayer = Player(score: BehaviorRelay(value: 2))
let players = PublishSubject<Player>()
players.asObserver()
.flatMap { (player) -> Observable<Int> in
player.score.asObservable()
}
/// subscribe 訂閱的不是players
/// 而是players事件中包含的每一個player.score
.subscribe(onNext:{
print($0)
})
.disposed(by: bag)
/// players 發(fā)送Event,對象是aPlayer,因此aPlayer被訂閱了
players.onNext(aPlayer)
/// aPlayer對象,發(fā)出事件,因?yàn)楸挥嗛喠?,所以能接收到并打?aPlayer.score.accept(3)
/// players 再次發(fā)出Event,對象是bPlayer,因此bPlayer也被訂閱了
/// 但是aPlayer的訂閱并沒有被取消
players.onNext(bPlayer)
/// aPlayer對象再發(fā)出事件,依然能被接收和打印
aPlayer.score.accept(4)
/// 打印結(jié)果:
1
3
2
4
flatMapLatest的實(shí)現(xiàn)
let aPlayer = Player(score: BehaviorRelay(value: 1))
let bPlayer = Player(score: BehaviorRelay(value: 2))
let players = PublishSubject<Player>()
players.asObserver()
.flatMapLatest { (player) -> Observable<Int> in
player.score.asObservable()
}
.subscribe(onNext:{
print($0)
})
.disposed(by: bag)
players.onNext(aPlayer)
aPlayer.score.accept(3)
/// players發(fā)起一個新的事件,會對bPlayer訂閱,并且同時取消上一次Event發(fā)送過來的aPlayer對象
players.onNext(bPlayer)
/// aPlayer被取消訂閱了,所以沒有打印
aPlayer.score.accept(4)
/// 打印結(jié)果:
1
3
2
總結(jié):如果flatMap接收事件Event包裹的元素也是一個Observable,并且不希望保存上一次的訂閱,則可以使用flatMapLatest
BehaviorRelay 、Subject的使用,可以參考這篇文章
勘誤:map、flatMap、flatMapLatest最終返回的都是一個可觀察序列(比如Observable、Driver),
不同的是map閉合函數(shù)返回的值,map函數(shù)會自動包裝一層可觀察序列
而flatMap、flatMapLatest閉合函數(shù)返回的必須是一個可觀察序列,
與map作用最大的區(qū)別是如果源可觀察序列攜帶的也是可觀察序列元素,那么flatMap、flatMapLatest就可以將閉合函數(shù)入?yún)⒌目捎^察序列直接返回,這樣達(dá)到一個“降維”的效果,訂閱flatMap、flatMapLatest轉(zhuǎn)換過后,就能直接獲取到可觀察序列中的元素(非可觀察序列),如有不明的請仔細(xì)查看案例2中的代碼
項(xiàng)目實(shí)操
看了很多篇文章,一直說flapMap、flapMapLatest很好用,但是一直都沒說要怎么用,那到底要怎么用了?(學(xué)以致用很重要)
首先要知道的是
flapMap和flapMapLatest返回的是一個可觀察序列Observable,一個可觀察序列,相等是一個“輸出源”,可以進(jìn)行訂閱監(jiān)聽,根據(jù)不同的輸出做不同的處理
繼續(xù)以登錄界面為例:一個手機(jī)號,一個驗(yàn)證碼,一個登錄按鈕,實(shí)現(xiàn)點(diǎn)擊登錄按鈕,發(fā)起網(wǎng)絡(luò)請求
# 未改進(jìn)的代碼:有一個最大的問題,請看代碼注釋
let telObservable = telInput.rx.text.orEmpty
let codeObservable = codeInput.rx.text.orEmpty
let tapObservable = loginBtn.rx.tap
let userNameAndCode = Observable<(String,String)>.combineLatest(telObservable, codeObservable) {
return ($0,$1)
}
/// 直接訂閱登錄按鈕的點(diǎn)擊事件,在訂閱中直接發(fā)起請求
/// 問題:不能優(yōu)雅的將網(wǎng)絡(luò)請求結(jié)果反饋到UI層中,將UI刷新。
/// 可能會通過多創(chuàng)建幾個Subject或可觀察序列的方式來實(shí)現(xiàn)反饋,這樣很多余,并且把問題復(fù)雜化
loginAction
.withLatestFrom(userNameAndCode)
.subscribe(onNext:{ (tel,code) in
/// 發(fā)起網(wǎng)絡(luò)請求
})
.disposed(by: disposebag)
# 改進(jìn)后的代碼
let telObservable = telInput.rx.text.orEmpty
let codeObservable = codeInput.rx.text.orEmpty
let tapObservable = loginBtn.rx.tap
let userNameAndCode = Observable<(String,String)>.combineLatest(telObservable, codeObservable) {
return ($0,$1)
}
/// 優(yōu)點(diǎn):
/// 第一:在flatMapLatest序列轉(zhuǎn)換的時候,就發(fā)起網(wǎng)絡(luò)請求
/// 第二:flatMapLatest序列轉(zhuǎn)換后的結(jié)果,必定是一個Observable可觀察序列,將這個序列拋到UI層訂閱,就能直接的將網(wǎng)絡(luò)請求結(jié)果反饋到UI中刷新處理
let loginResutl = loginAction.
.withLatestFrom(userNameAndCode)
.flatMapLatest { (arg0) -> Observable<String> in
/// 發(fā)起網(wǎng)絡(luò)請求,然后將結(jié)果包裹成Observable再發(fā)送出去
et (a, b) = arg0
return Observable.just("\(a)\(b)")
}
提示:
(1)Observable 與 Driver 對于 連著使用withLatestFrom、flatMapLatest操作符是不太一樣的請看下方
(2)對于:不會產(chǎn)生error事件、在主線程監(jiān)聽、并且共享狀態(tài)變化的極力推薦使用Driver替換Observable
let telObservable = telInput.rx.text.orEmpty.asDriver()
let codeObservable = codeInput.rx.text.orEmpty.asDriver()
let tapObservable = loginBtn.rx.tap.asDriver()
let userNameAndCode = Driver<(String,String)>.combineLatest(telObservable, codeObservable) {
return ($0,$1)
}
let loginResutl = loginAction
.withLatestFrom(userNameAndCode)
/// Driver 中的flatMapLatest閉合函數(shù)要求返回的是 :RxCocoa.SharedSequence,也就是Driver對象
/// Observable中flatMapLatest閉合函數(shù)要求非的是 :RxSwift.Observable
.flatMapLatest { (arg0) -> Driver<String> in
return Observable<String>.just("網(wǎng)絡(luò)請求結(jié)果").asDriver(onErrorJustReturn: "網(wǎng)絡(luò)異常")
}
PS:對于完整的登錄功能Demo,要本人再多幾次的review之后一定會出一篇文章,敬請期待??!
2021年3月19日更新
flatMap 與 map 的重要不同,flatMap可以起到一個降維的作用(flatMap與map的配合使用可以達(dá)到不錯的效果)
Observable.of(1,2,3)
.map { value in
return Observable.just(value * 10)
}
.flatMap({ value -> Observable<Int> in
print("flatMap輸出:\(value)")
return value
})
.map({(value) -> Int in
print("map輸出:\(value)")
return value
})
.subscribe(onNext:{ value in
})
.disposed(by: bag)
/// 打印結(jié)果:
/// flatMap輸出:RxSwift.(unknown context at $102f5ccb8).Just<Swift.Int>
/// map輸出:10
/// flatMap輸出:RxSwift.(unknown context at $102f5ccb8).Just<Swift.Int>
/// map輸出:20
/// flatMap輸出:RxSwift.(unknown context at $102f5ccb8).Just<Swift.Int>
/// map輸出:30