美文网首页Swiftswift
RxSwift_操作符_map、flatmap、flatMapL

RxSwift_操作符_map、flatmap、flatMapL

作者: MR_詹 | 来源:发表于2021-03-11 10:35 被阅读0次

    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<Swift.Int>
    RxSwift.(unknown context at $10dc3e738).Just<Swift.Int>
    RxSwift.(unknown context at $10dc3e738).Just<Swift.Int>
    
    
    /// 通过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<Int> 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<Source>(_ selector: @escaping (Self.Element) throws -> Source) 
    -> RxSwift.Observable<Source.Element> 
    where Source : RxSwift.ObservableConvertibleType
    

    flatMapLatest: 当源序列有新的事件发生的时候,flatMapLatest会自动取消上一个是事件的订阅,转到新的事件的订阅上面,而flatMap则会订阅全部

    #案例4:
    
    # 这种情况,flatMapLatest 与 flatMap 没什么不同,不能体现两者的区别
    Observable.of(1,2,3)
        .flatMapLatest { (value) -> Observable<Int> 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<Int>    
    }
    

    flatMap的实现

    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 发送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<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发起一个新的事件,会对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很好用,但是一直都没说要怎么用,那到底要怎么用了?(学以致用很重要)

    首先要知道的是flapMapflapMapLatest返回的是一个可观察序列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<String> 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<String> in
            return Observable<String>.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<Int> 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<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
    

    相关文章

      网友评论

        本文标题:RxSwift_操作符_map、flatmap、flatMapL

        本文链接:https://www.haomeiwen.com/subject/dykwqltx.html