map
操作符将源Observable的每个元素,通过提供的方法转换,然后返回含有转换后元素的Observable
#案例1:
Observable.of(1,2,3)
.map{
$0 * 10
}
.subscribe(onNext:{
print($0)
})
.disposed(by: bag)
/// 打印结果:
/// 10
/// 20
/// 30
flatMap
操作符会对源Observable的每一个元素应用一个转换方法,将他们转换成Observable,然后将这些Observable的元素合并之后再发送出来,即将其降维
成一个Observable序列
#案例2:
/// 如果Observable中的元素也是Observable
Observable.of(1,2,3)
.map { value in
return Observable.just(value * 10)
}
.subscribe(onNext:{
print($0)
})
.disposed(by: bag)
/// 打印结果:
RxSwift.(unknown context at $10dc3e738).Just
RxSwift.(unknown context at $10dc3e738).Just
RxSwift.(unknown context at $10dc3e738).Just
/// 通过flatMap降维,获取内部的元素值
Observable.of(1,2,3)
.map { value in
return Observable.just(value * 10)
}
.flatMap({
return $0
})
.subscribe(onNext:{
print($0)
})
.disposed(by: bag)
}
/// 打印结果:
/// 10
/// 20
/// 30
#案例3:
# 这种写法也是可以的,为什么?
# 请看下方讲解
Observable.of(1,2,3)
.flatMap { (value) -> Observable in
return Observable.just(value * 10)
}
.subscribe(onNext:{
print($0)
})
.disposed(by: bag)
/// 打印结果:
/// 10
/// 20
/// 30
/// 源码解析:
/// 首先selector函数型参数返回的是一个可观察对象,因为根据Source.Element可知,
/// 闭包函数返回的是一个可观察对象,如果此闭包函数返回的不是一个可观察对象则会报错
///
/// 第二:
/// selector闭包函数入参Self.Element,可以是普通的数据类型,也可以是可观察对象
/// 如果是普通数据类型,那么就必须要在闭包函数中将其包装为可观察对象
/// 如果其本身就是可观察对象,那么可以直接返回,不需要再包装
public func flatMap
flatMapLatest
: 当源序列有新的事件发生的时候,flatMapLatest会自动取消上一个是事件的订阅,转到新的事件的订阅上面
,而flatMap则会订阅全部
#案例4:
# 这种情况,flatMapLatest 与 flatMap 没什么不同,不能体现两者的区别
Observable.of(1,2,3)
.flatMapLatest { (value) -> Observable in
/// Observable.just 是一次性的可观察序列,发生完Event后,就直接Complete结束
/// 不能再发送第二个次的Event
/// 所以这不能很好的体现flatMapLatest的主要功能:取消上一次的事件订阅事件
return Observable.just(value * 10)
}
/// subscribe订阅的不是Observable.of(1,2,3)创建的可观察序列
/// 而是flatMap转变后的可观察序列
.subscribe(onNext:{
print($0)
})
.disposed(by: bag)
/// 打印结果:
/// 10
/// 20
/// 30
flatMapLatest 与 flatMap 使用区别案例
struct Player {
var score : BehaviorRelay
}
flatMap的实现
let aPlayer = Player(score: BehaviorRelay(value: 1))
let bPlayer = Player(score: BehaviorRelay(value: 2))
let players = PublishSubject()
players.asObserver()
.flatMap { (player) -> Observable in
player.score.asObservable()
}
/// subscribe 订阅的不是players
/// 而是players事件中包含的每一个player.score
.subscribe(onNext:{
print($0)
})
.disposed(by: bag)
/// players 发送Event,对象是aPlayer,因此aPlayer被订阅了
players.onNext(aPlayer)
/// aPlayer对象,发出事件,因为被订阅了,所以能接收到并打印
aPlayer.score.accept(3)
/// players 再次发出Event,对象是bPlayer,因此bPlayer也被订阅了
/// 但是aPlayer的订阅并没有被取消
players.onNext(bPlayer)
/// aPlayer对象再发出事件,依然能被接收和打印
aPlayer.score.accept(4)
/// 打印结果:
1
3
2
4
flatMapLatest的实现
let aPlayer = Player(score: BehaviorRelay(value: 1))
let bPlayer = Player(score: BehaviorRelay(value: 2))
let players = PublishSubject()
players.asObserver()
.flatMapLatest { (player) -> Observable in
player.score.asObservable()
}
.subscribe(onNext:{
print($0)
})
.disposed(by: bag)
players.onNext(aPlayer)
aPlayer.score.accept(3)
/// players发起一个新的事件,会对bPlayer订阅,并且同时取消上一次Event发送过来的aPlayer对象
players.onNext(bPlayer)
/// aPlayer被取消订阅了,所以没有打印
aPlayer.score.accept(4)
/// 打印结果:
1
3
2
总结:如果flatMap接收事件Event包裹的元素也是一个Observable,并且不希望保存上一次的订阅,则可以使用flatMapLatest
BehaviorRelay 、Subject
的使用,可以参考这篇文章
勘误
:map、flatMap、flatMapLatest最终返回的都是一个可观察序列(比如Observable、Driver),
不同的
是map闭合函数返回的值
,map函数会自动包装一层可观察序列
而flatMap、flatMapLatest
闭合函数返回的必须是一个可观察序列
,
与map作用最大的区别
是如果源可观察序列携带的也是可观察序列元素,那么flatMap、flatMapLatest就可以将闭合函数入参的可观察序列直接返回,这样达到一个“降维”的效果,订阅flatMap、flatMapLatest转换过后,就能直接获取到可观察序列中的元素(非可观察序列),如有不明的请仔细查看案例2中的代码
项目实操
看了很多篇文章,一直说flapMap、flapMapLatest很好用
,但是一直都没说要怎么用,那到底要怎么用了?(学以致用很重要)
首先要知道的是
flapMap
和flapMapLatest
返回的是一个可观察序列Observable
,一个可观察序列,相等是一个“输出源”
,可以进行订阅监听,根据不同的输出做不同的处理
继续以登录界面为例:一个手机号,一个验证码,一个登录按钮,实现点击登录按钮,发起网络请求
# 未改进的代码:有一个最大的问题,请看代码注释
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)
}
/// 直接订阅登录按钮的点击事件,在订阅中直接发起请求
/// 问题:不能优雅的将网络请求结果反馈到UI层中,将UI刷新。
/// 可能会通过多创建几个Subject或可观察序列的方式来实现反馈,这样很多余,并且把问题复杂化
loginAction
.withLatestFrom(userNameAndCode)
.subscribe(onNext:{ (tel,code) in
/// 发起网络请求
})
.disposed(by: disposebag)
# 改进后的代码
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)
}
/// 优点:
/// 第一:在flatMapLatest序列转换的时候,就发起网络请求
/// 第二:flatMapLatest序列转换后的结果,必定是一个Observable可观察序列,将这个序列抛到UI层订阅,就能直接的将网络请求结果反馈到UI中刷新处理
let loginResutl = loginAction.
.withLatestFrom(userNameAndCode)
.flatMapLatest { (arg0) -> Observable in
/// 发起网络请求,然后将结果包裹成Observable再发送出去
et (a, b) = arg0
return Observable.just("\(a)\(b)")
}
提示:
(1)Observable 与 Driver 对于 连着使用withLatestFrom、flatMapLatest操作符是不太一样的请看下方
(2)对于:不会产生error事件、在主线程监听、并且共享状态变化的极力推荐使用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闭合函数要求返回的是 :RxCocoa.SharedSequence,也就是Driver对象
/// Observable中flatMapLatest闭合函数要求非的是 :RxSwift.Observable
.flatMapLatest { (arg0) -> Driver in
return Observable.just("网络请求结果").asDriver(onErrorJustReturn: "网络异常")
}
PS:对于完整的登录功能Demo,要本人再多几次的review之后一定会出一篇文章,敬请期待!!
2021年3月19日更新
flatMap 与 map 的重要不同,flatMap可以起到一个降维
的作用(flatMap与map的配合使用可以达到不错的效果)
Observable.of(1,2,3)
.map { value in
return Observable.just(value * 10)
}
.flatMap({ value -> Observable in
print("flatMap输出:\(value)")
return value
})
.map({(value) -> Int in
print("map输出:\(value)")
return value
})
.subscribe(onNext:{ value in
})
.disposed(by: bag)
/// 打印结果:
/// flatMap输出:RxSwift.(unknown context at $102f5ccb8).Just
/// map输出:10
/// flatMap输出:RxSwift.(unknown context at $102f5ccb8).Just
/// map输出:20
/// flatMap输出:RxSwift.(unknown context at $102f5ccb8).Just
/// map输出:30