operator 也是 Rx 的基本元素, 通过它, 可以对 observable 中的事件序列进行操作.� 比如�可以去简单地进行加减. 通过 �operator 的链式表达, 可以实现许多复杂的�逻辑.
本部分首先学习 filter 类型的操作符, 然后是 transform 类型的操作符, 然后再看 combining 类型的操作符. 最后是一些基于时间的操作符, 比如延迟事件, 组合一段时间内的事件等等.
学习完本部分, 就具备了编写一些简单的 RxSwift app 的能力.
学习一个新的技术栈和�建造一栋摩天大楼是一个道理, 首先要打好打牢基础. 在之前的学习中已经学会了Observable, subject, RxSwift 中的内存管理等基础性内容. 下面就在这些的基础上, 再向上学习.
本章中会讲如何通过 RxSwift 的 过滤操作符来操作 next 事件, 这样最终观察者接收到的就是它自己想要接收的数据.
�概述
这里直入主题, 我们先来看再 RxSwift 中如何使用过滤操作符.
过滤型操作符
主要有如下三种:
- ignoreElements
- elementAt
- filter
ignoreElements 操作符
这个操作符的作用是过滤掉所有的 next 事件:
let disbag = DisposeBag()
let sbj = PublishSubject<Int>()
sbj
// .ignoreElements()
.subscribe(onNext: {
print($0)
}, onError: {
print($0)
}, onCompleted: {
print("complete")
}, onDisposed: {
print("dispose")
})
.addDisposableTo(disbag)
sbj.onNext(1)
sbj.onNext(2)
sbj.onCompleted()
若不加 ignoreElements, 则输出为:
1
2
complete
dispose
而加了的话:
complete
dispose
可以看到, ignoreElement 操作符的作用的确是将所有的 next 事件过滤掉.
elementAt 操作符
比如只想接收序列中的某个下标位置的元素(从0开始计数), 可以使用 elementAt 操作符.
elementAt 操作符的参数越界的话并不会崩溃, 只会打印错误信息.
上面这两个操作符都是属于过滤操作符中的"忽略"子类中的操作符.
若需要特殊的过滤条件, 则可以使用 filter 操作符.
filter 的原理是利用一个过滤条件块来, 在里面将过滤条件应用到所有的元素上, 这样即可对元素进行过滤. 只有当条件为真的元素才不会被过滤. 即需要保留的元素在应用条件时候的判断结果为真.
exampleOf("过滤", operation: {
let disposebag = DisposeBag()
let obsv = Observable.of(1, 2, 3, 4, 56)
obsv
.filter({ elem in
elem > 3
})
.subscribe(onNext: {
print($0)
}).addDisposableTo(disposebag)
})
上述首先对 observable 的事件序列应用了过滤操作符, 过滤掉所有小于 3 的元素. 输出为:
过滤
4
5
6
跳读操作符(skip)
如果想要自动跳过若干个元素时, 就可以使用 skip 操作符.
比如有三天的天气预报可以被观察, 但只想要今天的, 就可以跳过前两天的预报:
exampleOf("skip", operation: {
let obsv = Observable.from([1, 2, 3, 48])
obsv.skip(3).subscribe(onNext: {
print($0)
}).addDisposableTo(disBag)
})
输出为:
skip
4
8
实际上 skip 是一个操作符家族, 里面有诸如 skipWhile
, 它允许在 skip
的时候同时指定条件. 和 filter
不同的是, skipWhile
是遇到第一个不能 skip 的元素之后, 就不再进行判断了, 后面所有的元素都放行.
并且, skipWhile
的判断条件中, 如果满足条件的会被跳过. 结合起来即: 遇到第一个不满足判断条件的(返回值为 false
的)�元素及其后面的所有元素都会被观察者接收到.
比如下面的代码:
exampleOf("skipWhile", operation: {
let obsv = Observable.from([1, 2, 3, 48, 1, 2, 3])
obsv.skipWhile({ elem in
elem * 2 < 8
}).subscribe(onNext: {
print($0)
}).addDisposableTo(disBag)
})
则其输出为:
skipWhile
4
8
1
2
3
到目前为止, 所有的过滤都是使用一些静态条件. 但如果需要基于其他的 observable
来动态过滤元素呢?
下面就来介绍一些这样的 operator. 首先来看的是 skipUntil
, 它会持续在� "源 observable
" (就是观察者观察的那个 observable) 中跳过元素, 直到� "触发器 observable"(即另外一个 observable) 发送了 next 事件(而 complete 和 error 均无作用), 此时源 observable 中的后续的所有事件就不会再被跳过了.
比如如下代码:
exampleOf("skipUntil", operation: {
let sourceObsv = PublishSubject<Strin()
let triggerObsv = PublishSubject<Strin()
sourceObsv
.skipUntil(trigger)
.subscribe(onNext: {
print($0)
}).addDisposableTo(disBag)
sourceObsv.onNext("A")
sourceObsv.onNext("B")
triggerObsv.onNext("something")
sourceObsv.onNext("C")
})
skipUntil
C
Taking 操作符
Taking 的效果正好和 skip 相反.
比如如下代码:
exampleOf("taking", operation: {
let obsv = Observable.from([1, 2, 3, 48, 1, 2, 3])
obsv
.take(3)
.subscribe(onNext: {
print($0)
}).addDisposableTo(disBag)
})
则输出结果是:
taking
1
2
3
即 take(_ count:)
操作符的作用是只获取事件序列中的 count 个元素.
相应的, 也有 takeWhile
操作符, 它和 skipWhile
的区别是它一直去取满足条件的, 直到遇到第一个不满足条件的, 而 skipWhile
是满足条件的都被跳过, 直到遇到第一个不满足条件的.
另外还有些情况下需要� takeWhile
满足条件的同时满足�指定的元素下标要求, 此时就可以使用 takeWhileWithIndex
:
exampleOf("takeWhileWithIndex", operation: {
let obsv = Observable.from([5, 4, 8, 12, 3])
obsv
.takeWhileWithIndex({ value, idx in
return value > 3 && idx < 5
})
.subscribe(onNext: {
print($0)
})
.addDisposableTo(disBag)
})
输出为:
takeWhileWithIndex
5
4
8
即它去取满足条件的元素, 直到遇到第一个不满足条件的, 而后面的�都不通过.
另外还有 �skipWhileWithIndex
, 不同点是它是去跳过. 而和 skipUntil
类似, 有 takeUntil
操作符, 它的作用是一直�允许取 "源 observable" 中的元素, 直到 "触发器 observable" 发送 next 事件.
而它可以用在如下情况:
-
比如在 RxCocoa 中, 用于将观察者释放. 而无需装入 disposeBag. 但对于大多数情况而言, 放入 disposeBag 都是最佳的选择.
比如下面的代码:
someObservable .takeUntil(self.rx.deallocated) .subscribe(onNext: { print($0) })
即当自己被释放之前, 都可以获取到源 observable 的事件, 而当 触发器 �observable(这里是 RxCocoa 中的自己) 发送自己被释放的事件时, 就不能再获取源 observable 中的任何 next 事件了.
Distinct 操作符
下面来看一些可以防止多次重复元素通过的操作符.
先来看 distinctUntilChanged 操作符:
1---2---2---1
distinctUntilChanged
1---2------1
它的作用就是过滤掉序列中一次或多次相邻元素是相同的情况.
比如如下代码:
example(of: "Distinct Operator") {
Observable.of("A", "B", "A", "A", "B", "B")
.distinctUntilChanged()
.subscribe(onNext: {
print($0)
})
.addDisposableTo(disposeBag)
}
输出为:
--- Example of: Distinct Operator ---
A
B
A
B
而操作符的判断依据是根据 Equatable 协议, 由于 String 是实现了该协议的, 故可以对 String 类型的序列使用.
但如果是没有实现该协议的元素类型, 要想判断的话, 可以先去实现该协议. 或者是可以使用 distinctUntilChanged(_:)
提供判断条件即可.
example(of: "Distinct with condition") {
let elem0 = MyElemType(value: 1, name: "elem0")
let elem1 = MyElemType(value: 2, name: "elem1")
let elem2 = MyElemType(value: 2, name: "elem2")
let elem3 = MyElemType(value: 1, name: "elem3")
Observable.of(elem0, elem1, elem2, elem3)
.distinctUntilChanged({ (elemA, elemB) -Bool in
elemA.value == elemB.value
})
.subscribe(onNext: {
print($0)
})
.addDisposableTo(disposeBag)
}
输出为:
--- Example of: Distinct with condition ---
MyElemType(value: 1, name: "elem0")
MyElemType(value: 2, name: "elem1")
MyElemType(value: 1, name: "elem3")
可以看到, 通过条件来判断两个元素是否相等操作起来十分方便.