学习一门新技术有点像建造摩天大楼。在你摸到天空之前必须建立坚实的基础。现在已经建立了一个坚实的RxSwift基础,是时候一层一层的学习基础知识和技巧了。
本章将教你RxSwift的过滤操作符,您可以使用条件约束.next 事件,所以用户只接收要处理的elements。如果你使用过filter(_:)方法,你已经成功一半了,如果没有也不要担心,在本章结束你可能会成为一个专家。
Ignoring operators
闲话少说,直接看RxSwift中有用的filtering操作符,从ignoreElements开始。如下面图表描述的一样,ignoreElements会忽视.next事件。然而,它允许stop事件通过,即.completed或.error事件。
示例:
example(of: "ignoreElements") {
// 1
let strikes = PublishSubject()
let disposeBag = DisposeBag()
// 2
strikes
.ignoreElements()
.subscribe { _ in
print("You're out!")
}
.addDisposableTo(disposeBag)
}
他们做了什么:
1.创建一个变量strikes subject
2.订阅strikes的所有事件,忽略所有的.next事件
当一个observable被.completed 或者 .error事件终止时你想得到通知ignoreElements就会很有用,
strikes.onNext("X")
strikes.onNext("X")
strikes.onNext("X")
尽管发送了很多次,但是没有打印任何东西,因为你忽略了.next事件。需要添加.completed事件来通知订阅者。如下:
strikes.onCompleted()
现在订阅者接收到.completed事件,打印出:
--- Example of: ignoreElements ---
You're out!
有些时候你只想处理observable发送的第n(按顺序)个元素。你可以使用elementAt,用下标获取你想接收的element,然后忽略其他。下图中,只有下标为1的element会通过,所以只会通过第二个element。
example(of: "elementAt") {
// 1
let strikes = PublishSubject()
let disposeBag = DisposeBag()
// 2
strikes
.elementAt(2)
.subscribe(onNext: { _ in
print("You're out!")
})
.addDisposableTo(disposeBag)
}
一步一步演示:
1.创建一个subject
2.订阅.next事件,忽略所有.next事件,除开第三个(下标为2)。
现在你向subject上添加新的strikes,你就会知道是触发的哪一个,代码如下:
strikes.onNext("X")
strikes.onNext("X")
strikes.onNext("X")
打印:
--- Example of: elementAt ---
You're out!
ignoreElements和elementAt都可以过滤observable发送的elements。filter就像是一个predicate闭包,它去匹配每个element,只有当匹配通过时才允许通过。
查看一下图表,只有1和2通过,因为filter的predicate只允许小于3的elements通过。
示例:
example(of: "filter") {
let disposeBag = DisposeBag()
// 1
Observable.of(1, 2, 3, 4, 5, 6)
// 2
.filter { integer in
integer % 2 == 0
}
// 3
.subscribe(onNext: {
print($0)
})
.addDisposableTo(disposeBag)
}
1.创建了一个定于integers的observable
2.用filter操作符匹配约束条件来防止奇数通过。filter携带一个Bool的predicate,允许为true的元素通过和防止false的元素通过
3.订阅和打印出通过filter predicate的elements
--- Example of: filter ---
2
4
6
Skipping operators
你可能会需要跳过某一个elements。就像天气预报一样,也许你不需要每小时的预测数据,因为你一直在房间里。skip操作符允许从第一个到第几个忽略。下表展示了忽略前2个elements。
示例:
example(of: "skip") {
let disposeBag = DisposeBag()
// 1
Observable.of("A", "B", "C", "D", "E", "F")
// 2
.skip(3)
.subscribe(onNext: {
print($0) })
.addDisposableTo(disposeBag)
}
1.创建一个字母型的observable
2.用skip跳过前3个elements和订阅.next事件
在忽略前3个elements之后只剩下D,E:
--- Example of: skip ---
D
E
F
skip操作符有几种。像filter,skipWhile允许你用一个predicate来觉得什么需要被skipped。然而,不像filter从订阅就开始filter,skipWhile会直接跳过,直到遇到被通过的元素,从那时起就会都会通过,跳过不在起作用了。
skipWhile返回true会使element被跳过,返回false会通过。和filter相反。下图中,1直接被跳过,2允许被通过,3会被通过因为skipWhile不在起作用了。
示例:
example(of: "skipWhile") {
let disposeBag = DisposeBag()
// 1
Observable.of(2, 2, 3, 4, 4)
// 2
.skipWhile { integer in
integer % 2 == 0
}
.subscribe(onNext: {
print($0)
})
.addDisposableTo(disposeBag)
}
1.创建一个integers的observable
2.用skipWhile来过滤元素直到遇到基数
skip只是跳过第一个element通过之前的所有elements,然后就允许所有的elements通过:
--- Example of: skipWhile ---
3
4
4
到目前为止,过滤的都是基于一些静态条件。如果你想基于一些observable来动态过滤elements呢?下面这两个操作符可以做到。第一个是skipUntil,它将会保持跳过observable上的elements直到触发observable发送事件。下图中,skipUntil忽略了observable上的elements直到第二行的observable触发.next事件。然后从此时开始就停止跳过和允许所有elements通过。
示例:
example(of: "skipUntil") {
let disposeBag = DisposeBag()
// 1
let subject = PublishSubject()
let trigger = PublishSubject()
// 2
subject
.skipUntil(trigger)
.subscribe(onNext: {
print($0) })
.addDisposableTo(disposeBag)
}
1.创建subject,创建另一个subject(trigger)来触发第一个subject
2.用skipUntil来跳过subject。直到trigger触发,skipUnti才停止跳过
添加下面两个.next事件:
subject.onNext("A")
subject.onNext("B")
不会有任何打印,因为都被跳过了,现在添加一个新的.next事件到trigger上:
trigger.onNext("X")
这使skipUntil停止跳过。从现在起,所有elements都将被通过:
subject.onNext("C")
打印结果:
--- Example of: skipUntil ---
C
Taking operators
Taking和skipping相反。当你想得到某个elements,RxSwift已经提供了。第一个taking操作符就是take,如下图所示,将会取前两个elements。
示例:
example(of: "take") {
let disposeBag = DisposeBag()
// 1
Observable.of(1, 2, 3, 4, 5, 6)
// 2
.take(3)
.subscribe(onNext: {
print($0) })
.addDisposableTo(disposeBag)
}
1.创建integers型的observable
2.用take取前3个elements
打印:
--- Example of: take ---
1
2
3
takeWhile操作符和skipWhile相似,有时你想关联element的下标,这有另一中takeWhile类型的是:takeWhileWithIndex。当predicate为真时会通过,也会通过你关联下标的元素。
下图中,要求值大于1和下标大于1,所以2能通过第一个要求,但是下标是1不能通过第二个要求。
示例:
example(of: "takeWhileWithIndex") {
let disposeBag = DisposeBag()
// 1
Observable.of(2, 2, 4, 4, 6, 6)
// 2
.takeWhileWithIndex { integer, index in
// 3
integer % 2 == 0 && index < 3
}
// 4
.subscribe(onNext: {
print($0)
})
.addDisposableTo(disposeBag)
}
1.创建一个integers型的observable。
2.用takeWhileWithInde操作符,创建一个接受元素和元素下标的闭包
3.只能通过偶数和下标小鱼3的元素
4.订阅和打印
结果就是你直接受到了偶数和下标小于3的元素.
--- Example of: takeWhileWithIndex ---
2
2
4
这里也有像这样的skipWhileWithIndex操作符,不像skipping,也不像taking。就像skipUntil,也有一种takeUntil操作符,如图所示,从observable上获取元素直到trigger发送element。
示例:
example(of: "takeUntil") {
let disposeBag = DisposeBag()
// 1
let subject = PublishSubject()
let trigger = PublishSubject()
// 2
subject
.takeUntil(trigger)
.subscribe(onNext: {
print($0) })
.addDisposableTo(disposeBag)
// 3
subject.onNext("1")
subject.onNext("2")
}
1.创建subjec和trigger。
2.用takeUntil获取元素直到trigger触发。
3.向subject中添加两个element
打印:
--- Example of: takeUntil ---
1
2
现在向trigger添加element,向element中添加另一个element:
trigger.onNext("X")
subject.onNext("3")
X终止了taking,所以3不被允许通过,不会打印出来。
和RxCocoa库的api用法一致,takeUntil也会用来dispose订阅,代替把它添加到dispose bag.
在observables用if-then-but-what很容易导致内存泄漏,或者将你的代码复杂化。单纯的将subscription添加到dispose bag是一个好的方式。
为了演示完整,你怎样使用RxCocoa中的takeUntil(不要将这段代码加入到示例中,因为不会编译通过):
someObservable
.takeUntil(self.rx.deallocated)
.subscribe(onNext: {
print($0) })
上面一段代码中,trigger会造成takeUntil停止taking和销毁自己,典型的就是在 view controller 或 view model中。
Distinct operators
如图所示,distinctUntilChanged可以防止连续重复的items通过,所以只有第一个2通过。
示例:
example(of: "distinctUntilChanged") {
let disposeBag = DisposeBag()
// 1
Observable.of("A", "A", "B", "B", "A")
// 2
.distinctUntilChanged()
.subscribe(onNext: {
print($0) })
.addDisposableTo(disposeBag)
}
1.创建字符串类型的observable
2.用distinctUntilChange来防止连续重复的通过。
distinctUntilChanged仅仅用来防止连续重复的。所以第二个A被阻止了因为和第一个相同,但是最后一个A可以通过,因为它在不同的B之后,打印结果包括第一个A,第一个B和最后一个A。
--- Example of: distinctUntilChanged ---
A
B
A
这些都是字符串,遵循Equatable。所以,这些元素比较遵循Equatable的实现。然而,您可以distinctUntilChanged(_:)提供自己的自定义比较逻辑,未命名的参数是一个比较器。
下图中,基于对象的value属性比较。
用distinctUntilChanged(_:)添加一个简短的案例:
example(of: "distinctUntilChanged(_:)") {
let disposeBag = DisposeBag()
// 1
let formatter = NumberFormatter()
formatter.numberStyle = .spellOut
// 2
Observable.of(10, 110, 20, 200, 210, 310)
// 3
.distinctUntilChanged { a, b in
// 4
guard let aWords = formatter.string(from:a)?.components(separatedBy: " "),
let bWords = formatter.string(from: b)?.components(separatedBy: "")
else {
return false
}
var containsMatch = false
// 5
for aWord in aWords {
for bWord in bWords {
if aWord == bWord {
containsMatch = true
break
}
}
}
return containsMatch
}
// 4
.subscribe(onNext: {
print($0)
})
.addDisposableTo(disposeBag)
}
--- Example of: distinctUntilChanged(_:) ---
10
20
200