RxSwift学习-05-高阶函数(上)

565 阅读5分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第5天,点击查看活动详情

  • 本文主要介绍一些高阶函数的使用和原理

1. 组合操作符

1.1 combineLatest

对于组合操作符我们经常会用到的是比如登陆

let ob1 = nameTextfield.rx.text.orEmpty.map{ (str) -> Bool in

            str.count > 6

        }

        let ob2 = passwordTextField.rx.text.orEmpty.map{ (str) -> Bool in

            str.count > 6

        }

        

        Observable.combineLatest(ob1, ob2){$0&&$1}

            .bind(to: self.sureBtn.rx.isEnabled)

            .disposed(by: disposBag)

        

        sureBtn.rx.tap.subscribe(onNext: {()in

            print("点击了")

        }).disposed(by: disposBag)

当我们的姓名输入框和密码输入框都符合要求是才可以点击.下面的例子更能说明他们的关系

       let strSub = PublishSubject<String>()

        let intSub = PublishSubject<Int>()

        Observable.combineLatest(strSub, intSub) { strElement, intElement in

            

            "\(strElement):\(intElement)"

        }.subscribe(onNext:{(combineStr) in

            print(combineStr)

        })

        .disposed(by: disposBag)

        

        strSub.onNext("Hello")

        strSub.onNext("this")

        strSub.onNext("is")

        strSub.onNext("combine")

        intSub.onNext(03)

        intSub.onNext(25)

        strSub.onNext("hello")

打印结果

image.png

只会保存后发送的信号,并且组合信号中,只有都发送了才会到共同订阅的.

  • 源码分析

image.png

这里是把2个序列转换为一个序列,通过闭包把2个序列的元素element转换为新的element进行订阅。这里2个组合就是CombineLatest2,3个的话就是CombineLatest3,依次类推最多8个。这里我们就以2个为例进行下面的分析

  • CombineLatest2 image.png

首先初始化的时候保存序列1和序列2,并保存组合产生新element的闭包。以及一个订阅的时候run的方法。

  • CombineLatestSink2_

image.png

初始化的时候

1.2 zip

相比combineLast ,对于zip也是组合操作信号,不过zip是会保存我们发送的信号结果,2个都满足的时候会发送,但是是按顺序进行发送。

     let strSub = PublishSubject<String>()

        let intSub = PublishSubject<Int>()
Observable.zip(strSub,intSub){strElement, intElement in

            

            "\(strElement):\(intElement)"

            

        }.subscribe(onNext:{print($0)}).disposed(by: disposBag)

        intSub.onNext(11)

        intSub.onNext(12)

        strSub.onNext("test11")

        intSub.onNext(13)

        strSub.onNext("test12")

        strSub.onNext("test13")

运行结果

image.png

每次会把单个信号结果记录下来,之后都满足的话则再依次取出进行合并我们要的element,发送给订阅者。

1.3 startWith

startWith也是合并信号序列,但是订阅的时候是倒叙的方式,相当于从后往前发送信号给订阅者

Observable.of(1,2,3,4)

            .startWith(5)

            .startWith(6)

            .startWith(7)

            .startWith(8,9,10)

            .subscribe(onNext:{print($0)})

            .disposed(by: disposBag)

运行结果:

image.png

1.4 merge

merge 把来源的可观察序列中的元素组合成一个新的可观察序列,之后会观察每个源的发送的信号并订阅。相当于一个订阅者订阅了多个序列,每个序列发送了信号,订阅者都能收到信号。

let subject1 = PublishSubject<Int>()

        let subject2 = PublishSubject<Int>()

        Observable.of(subject1,subject2)

            .merge()

            .subscribe(onNext:{print($0)})

            .disposed(by: disposBag)

        

        subject1.onNext(1)

        subject2.onNext(2)

        subject2.onNext(3)

运行结果:1,2,3

1.5 switchLatest

switchLatest 就类似我们的switch开关,通过切换观察源来订阅不同序列发送的信号,对于没有订阅的

let switchLatestSub1 = BehaviorSubject(value: "1")

        let switchLatestSub2 = BehaviorSubject(value: "2")

        let switchLatestSub = BehaviorSubject(value: switchLatestSub2)

        

        switchLatestSub.asObservable()

            .switchLatest()

            .subscribe(onNext:{print($0)})

            .disposed(by: disposBag)

        

        switchLatestSub1.onNext("3")

        switchLatestSub1.onNext("41")


        switchLatestSub1.onNext("45")//打印1,,3,4


        switchLatestSub2.onNext("5")

        //切换订阅源

        switchLatestSub.onNext(switchLatestSub1)//打印5

        switchLatestSub2.onNext("6")

        switchLatestSub1.onNext("7")

对于我们没有订阅的源,发送信号会保存最新的一次信号,下次切换的时候会收到订阅。

2. 映射操作符

映射操作符和swift的高阶函数类似

2.1 map

let ob = Observable.of(1,2,3)

        ob.map{$0+2}

            .subscribe(onNext: {print($0)})

            .disposed(by: disposBag)//打印结果3,4,5

发送element前,通过map对元素进行transform 调用函数之后发送到订阅者

2.2 flatMap

在swift中flatMap会把类似二维的数组变成一维压平的感觉。而RxSwift中将可观测序列发射的元素转换为可观测序列,并将两个可观测序列的发射合并为一个可观测序列。

//结构体
struct Player {

    let score: BehaviorRelay<Int>

    

    init(score:Int) {

        

        self.score = BehaviorRelay(value: score)

    }

    

}

        let playerA = Player.init(score: 20)

        let playerB = Player.init(score: 30)

        let player = BehaviorRelay(value: playerA)

        

        player.asObservable()

            .flatMap { (player:Player) ->Observable<Int> in

                player.score.asObservable()

            }.subscribe { (num) in

                print(num)

            } onError: { (error) in

                print(error)

            } onCompleted: {

                print("completed")

            } onDisposed: {

                print("disposed")

            }.disposed(by: disposBag)

        

        playerA.score.accept(40)

        playerB.score.accept(50)

        player.accept(playerB)

        playerB.score.accept(60)
        playerA.score.accept(70)

打印结果

image.png

我们把订阅的类型Player转换了Int类型Observable序列进行订阅,最后都订阅了话两个可观测序列的发射合并为一个可观测序列。比如在网络请求中为了在请求一个网络资源后,根据服务器返回的结果对原事件序列进行变换,flatMap就是最好的选择。

2.3 flatMapLatest

flatMap和flatMapLatest的区别是,当原序列有新的事件发生的时候,flatMapLast 会自动取消上一个事件的订阅,转到新的事件的订阅上面, flatMap则会订阅全部。

image.png

flatMapLatest实际上是map和switchLatest操作符的组合。

2.4 scan

从初始就带有一个默认值开始,然后对可观察序列发出的每个元素应用累加器闭包,并以单个元素可观察序列的形式返回每个中间结果

Observable.of(10,20,30,40)

            .scan(2) { aggregateValue, newValue in

                aggregateValue + newValue// 类似迭代器,不断累加 10+2, 10+2+20,10+2+20+30,10+2+20+30+40

            }

            .subscribe(onNext:{print($0)})

            .disposed(by: disposBag)

打印结果

image.png

3. 过滤条件操作符

3.1 filter

filter和swift类似,在rx中,在发送序列element的时候判断是否满足我们的判读条件,满足的话进行发送,否则不发送


        Observable.of(1,2,3,4,5)

            .filter{$0%2 > 0}

            .subscribe(onNext: {print($0)})

            .disposed(by: disposBag)

打印结果:1,3,5

3.2 distinctUntilChanged

distinctUntilChanged: 序列元素发生改变的时候,类似于去重发送信号

Observable<Int>.of(1,2,2,3,3,3,4,4,4,4)

            .distinctUntilChanged()

            .subscribe(onNext: {print($0)})

            .disposed(by: disposeBag)

打印结果1,2,3,4.

3.3 elementAt

elementAt: 仅在可观察序列发出的所有元素的指定索引处发出元素

Observable<Int>.of(1,2,3,4,5)

            .element(at: 1)

            .subscribe(onNext: {print($0)})

            .disposed(by: disposeBag)

打印结果为2

3.4 single

single: 只发出可观察序列发出的第一个元素(或满足条件的第一个元素)。如果可观察序列发出多个元素,将抛出一个错误。

Observable.of(1,2,3,4,5)

            .single()

            .subscribe { num in

                print(num)

            } onError: { error in

                print(error)

            } onCompleted: {

                print("comleter")

            } onDisposed: {

                print("disposed")

            }

            .disposed(by: disposeBag)

打印结果:

image.png

因为多个条件满足所以会发送error信号,我们添加判断条件

image.png

single 表示单个信号,如果多个条件满足就会发送error信号

image.png

3.5 take

take: 只从一个可观察序列的开始发出指定数量的元素。 上面signal只有一个序列 在实际开发会受到局限 这里引出take想几个就几个

Observable.of(1,2,3,4,5)

            .take(3)

            .subscribe(onNext: {print($0)})

            .disposed(by: disposeBag)

结果:打印前3个元素

3.6 takeLast

takeLast: 仅从可观察序列的末尾发出指定数量的元素

Observable.of(1,2,3,4,5)

            .takeLast(1)

            .subscribe(onNext: {print($0)})

            .disposed(by: disposeBag)

打印结构为5,相较于take相当于序列的长度减去我们takeLast()的长度开始发送

3.7 take(while:)

takeWhile:满足指定条件即可从序列开始发送信号


        Observable.of(1,2,3,4,5)

            .take(while: {$0 < 4})

            .subscribe(onNext: {print($0)})

            .disposed(by: disposeBag)
           // 打印结果:1,2,3

从开始的的条件开始发送信号,满足就会发送,一开始就不满足就不会发送

image.png

3.8 take(until:)

从源可观察序列发出元素,直到参考可观察序列发出元素

//例子1
Observable.of(1,2,3,4,5)

            .take(until: {$0 > 4})

            .subscribe(onNext: {print($0)})

            .disposed(by: disposeBag)

     
     //例子2

        let source1 = PublishSubject<String>()

        let source2 = PublishSubject<String>()

        

        source1.take(until: source2)

            .subscribe(onNext: {print($0)})

            .disposed(by: disposeBag)

        

        source1.onNext("hello")

        source1.onNext("this")

        source1.onNext("is")

        source2.onNext("good")

        source1.onNext("day")

对于例子1可以看到和上面的take(while:)相反的效果,它是满足知道我们的条件满足就停止发送信号。对于例子2相当于我们用另一个序列控制,当soucre2发送了序列信号就终止了source1序列。比如我页面销毁了,就不能发送请求了。
打印结果

image.png

3.9 skip

skip:跳过开始的几次序列信号

Observable.of(1,2,3,4,5)

            .skip(1)

            .subscribe(onNext: {print($0)})

            .disposed(by: disposeBag)

打印结果:2,3,4,5。 对于我们textFiled经常使用,跳过第一次空的初始化信号。

3.10 skip(while:)

skip(while:):

Observable.of(1,2,3,4,5)

            .skip(while: {$0<4})//当发送的序列信号元素不满足就跳过

            .subscribe(onNext: {print($0)})

            .disposed(by: disposeBag)

打印结果:4,5

3.11

let sourceSequence = PublishSubject<Int>()

        let relevanceSequence = PublishSubject<Int>()

        

        sourceSequence.skip(until: relevanceSequence)

            .subscribe(onNext: {print($0)})

            .disposed(by: disposBag)

        

        sourceSequence.onNext(1)

        sourceSequence.onNext(2)

        

        relevanceSequence.onNext(3) //开关,控制

        

        sourceSequence.onNext(4)

        sourceSequence.onNext(5)

打印结果:4,5
可以发现和我们的takeUntil相反,skip(until:)是收到关联序列发送序号后该序列才会收到订阅的信号,类似一个开关

4. 总结

以上就是部分高阶函数的使用主要分为组合操作符映射操作符,和过滤操作符,记录下,未完待续。。。