RxSwift操作符

1,001 阅读2分钟

RxSwift操作符

RxSwift的操作符就是负责转换的,使用合适的操作符能帮我们减少代码量并提高生产力 在这里,我将总结一下常用的过滤操作符,转换操作符,合并操作符filter,distinctUntilChanged,map,combineLatest的用法

1.过滤操作符

过滤操作符用于过滤事件,我们可以使用过滤操作符把订阅者不关心的事件给过滤掉。常用的过滤炒作符有filter和distinctUntilChanged

1.1 filter操作符常用于规则过滤不需要的事件。以下所示,有 2, 23, 5, 60,1, 31 我想把小于10的数过滤掉,就可以通过filter设置过滤规则,然后打印出来的数字就是23, 60 ,31]

Observable.of(2, 23,  5, 60, 1 ,31)
            .filter {$0 > 10 }
            .subscribe {
                print($0)
            }.disposed(by: disposeBag)

image-20210710172520172.png

1.2 distinctUntilChanged用于把相同的事件过滤掉,如下面例子第二个1和第四个二使用distinctUntilChanged就可以把它们给过滤掉,然后打印1,2,1

Observable.of(1,1,2,2,1)
            .distinctUntilChanged()
            .subscribe {
                print($0)
            }.disposed(by: disposeBag)

image-20210710174221900.png

2.转换操作符

转换操作符非常实用,能帮组我们从一种数据类型转变成另外一种类型 其中常用的转换操作符有 map,compactMap,flapMap

2.1 map是一种十分常用的操作符,可用于从一种类型转换成另外一种类型,下面打印会打印出"String: 1" 和 "String: 2"

Observable.of(1,2)
            .map { "String:" + String($0) }
            .subscribe {
                print($0)
            }.disposed(by: disposeBag)

image-20210710175029860.png

2.2 compactMap常用于过滤掉值为nil的操作符,可以把compactMap理解为同时使用filter和map两个操作符,filter把nil值去掉,而map把非空的值进行转换,我把字符串的值转换为数值类型,并把不转换成功的值过滤掉,因此被过滤掉了,最终打印为1, 2

Observable.of("1", "not-a-number", "2")
            .compactMap { Int($0) }
            .subscribe {
                print($0)
            }.disposed(by: disposeBag)

image-20210710180307485.png

2.3 flatMap用于把两层的Observable序列合并到一层,通过下面例子来解析到底怎样合并

struct TemperatureSenor {
    let temperature: Observable<Int>
}
let sensor1 = TemperatureSenor(temperature: Observable.of(21, 23))
        let sensor2 = TemperatureSenor(temperature: Observable.of(22, 25))
        
        Observable.of(sensor1, sensor2)
            .flatMap { $0.temperature }
            .subscribe {
                print($0)
            }.disposed(by: disposeBag)

在这个例子中,我定义了一个TemperatureSensor的结构体,用来表示收集温度的传感器,该结构体包含了一个类型为Observable的temperature的属性。假如天气站有多个这样的传感器,我们要把他们的温度信息合并到一个单独的Observable序列中方便统计,此时就可以使用flatMap来完成任务。

具体来说,我们在flatMap方法的闭包里面返回temperature属性,由于该属性是一个Observable对象,因此flatMap方法会把这些序列统一合并到一个单独的 Observable 序列里面,并打印出 21、23、22、25。

image-20210710182251367.png

3.合并操作符

合并操作符用于组装与合并多个Observable序列,常用的有startWith,concat和merge

3.1 startWIth可以使订阅者在接收到Observable序列的事件前,先收到传给startWith方法 的事件,例如在下面的例子中,我们把 3 和 4 传递给startWith。那么在执行过程中,会先把 3 和 4 事件发送给订阅者,其运行效果为 3、4、1、2

Observable.of(1, 2)
            .startWith(3,4)
            .subscribe {
                print($0)
            }.disposed(by: disposeBag)

image-20210710183310778.png

在日常中,我们可以通过startWith方法,把加载事件插入网络数据事件之前,以此来保持UI状态的自动更新

3.2 concat能把多个Observable序列按顺序合并在一起。例如,在下面的例子中我们合并了两个 Observable 序列,第一个包含 1 和 2,第二个包含 3 和 4,那么执行的时候会打印 1、2、3、4

Observable.of(1, 2)
            .concat(Observable.of(3, 4))
            .subscribe {
                print($0)
            }.disposed(by: disposeBag)

image-20210710184826852.png

3.3 merge, 常用来合并多个Observable序列的操作符,和Contact不一样的地方是它能保持原来事件的顺序,

下面这个例子,我们调用merge方法把两个 PublishSubject 合并在一起,然后不同的 PublishSubject 会分别发出不同的next事件,订阅者根据事件发生的顺序来接收到相关事件。时会打印 1、2、11、3、12、13、4。

let first = PublishSubject<Int>()
        let second = PublishSubject<Int>()
        
        Observable.of(first, second)
            .merge()
            .subscribe {
                print($0)
            }.disposed(by: disposeBag)
        
        first.onNext(1)
        first.onNext(2)
        second.onNext(11)
        first.onNext(3)
        second.onNext(12)
        second.onNext(13)
        first.onNext(4)

我们调用merge方法把两个 PublishSubject 合并在一起,然后不同的 PublishSubject 会分别发出不同的next事件,订阅者根据事件发生的顺序来接收到相关事件。如下图所示,程序执行时会打印 1、2、11、3、12、13、4。

image-20210710184030878.png

3.4 combineLatest会把两个Observable序列里最后的事件合并起来,在程序执行过程中,当其中一个 PublishSubject 发出next事件时,就会从另外一个 PublishSubject 取出其最后一个事件,然后调用combineLatest方法的闭包,把这两个事件合并起来并通知订阅者。上述的例子在执行时会打印 1a、2a、2b、2c、3c、4c

 let first = PublishSubject<String>()
        let second = PublishSubject<String>()
        
        Observable.combineLatest(first, second) { $0 + $1}
            .subscribe {
                print($0)
            }.disposed(by: disposeBag)
        
        first.onNext("1")
        second.onNext("a")
        first.onNext("2")
        second.onNext("b")
        second.onNext("c")
        first.onNext("3")
        first.onNext("4")

在程序执行过程中,当其中一个 PublishSubject 发出next事件时,就会从另外一个 PublishSubject 取出其最后一个事件,然后调用combineLatest方法的闭包,把这两个事件合并起来并通知订阅者。上述的例子在执行时会打印 1a、2a、2b、2c、3c、4c

image-20210710185816136.png

在实际开发中,combineLatest方法非常实用。我们可以用它来监听多个 Observable 序列,然后组合起来统一更新状态。例如在一个登录页面里面,我们可以同时监听用户名和密码两个输入框,当它们同时有值的时候才激活登录按钮。

3.5 zip也能用于合并两个 Observable 序列

  • 和 combineLatest 不一样的地方是, zip 只会把两个 Observable 序列的事件配对合并。就像两队小朋友,排在前头的手牵手来到一个新队列。一旦出来就不再留在原有队列了。

    let first = PublishSubject<String>()
            let second = PublishSubject<String>()
            
            Observable.zip(first, second) { $0 + $1}
                .subscribe {
                    print($0)
                }.disposed(by: disposeBag)
            
            first.onNext("1")
            second.onNext("a")
            first.onNext("2")
            second.onNext("b")
            second.onNext("c")
            first.onNext("3")
            first.onNext("4")
    

    有两个 PublishSubject,其中first发出 1、2、3、4,而second发出 a、b、c。zip方法会返回它们的合并事件 1a、2b、3c。由于first所发出next("4")事件没有在second里面找到对应的事件,所以合并后的 Observable 序列只有三个事件

image-20210710190352316.png