本节的操作符处理发布者发出的整个值域,但不会发布他们原有的任何值。相反,这些运算符会发出一个不同的值,代表对发布者的一些查询处理。一个很好的例子是计数操作符count。
count
计数操作符将发出一个单一的值 - 一旦发布者发送一个 .finished 完成事件,上游发布者发出的值的数量。
在playground中加入如下代码:
// 1
let publisher = ["A", "B", "C"].publisher
// 2
publisher
.print("publisher")
.count()
.sink(receiveValue: { print("I have \($0) items") })
.store(in: &subscriptions)
运行playground,得到结果:
publisher: receive subscription: (["A", "B", "C"])
publisher: request unlimited
publisher: receive value: (A)
publisher: receive value: (B)
publisher: receive value: (C)
publisher: receive finished
I have 3 items
Demo中使用 count() 发出一个值,指示上游发布者发出的值的数量
contains
如果上游发布者发出了指定的值,则 contains 操作符将发出 true 并取消订阅,如果没有发出的值等于指定的值,则操作符发出false。
在playground加入如下代码:
// 1
let publisher = ["A", "B", "C", "D", "E"].publisher
let letter = "C"
// 2
publisher
.print("publisher")
.contains(letter)
.sink(receiveValue: { contains in
// 3
print(contains ? "Publisher emitted \(letter)!"
: "Publisher never emitted \(letter)!")
})
.store(in: &subscriptions)
运行playground,得到结果:
publisher: receive subscription: (["A", "B", "C", "D", "E"])
publisher: request unlimited
publisher: receive value: (A)
publisher: receive value: (B)
publisher: receive value: (C)
publisher: receive cancel
Publisher emitted C!
contains(where:)
当希望为contains增加查找条件时,或检查是否存在不符合 Comparable 的发出值时。可以使用 contains(where:)
在playground中加入如下代码:
// 1
struct Person {
let id: Int
let name: String
}
// 2
let people = [
(123, "Shai Mishali"),
(777, "Marin Todorov"),
(214, "Florent Pillet")
]
.map(Person.init)
.publisher
// 3
people
.contains(where: { $0.id == 800 })
.sink(receiveValue: { contains in
// 4
print(contains ? "Criteria matches!"
: "Couldn't find a match for the criteria")
})
- 定义一个带有 id 和名称的 Person 结构。
- 创建一个发布三个不同的 People 实例的发布者。
- 使用 contains 查看其中任何一个的 id 是否为 800。
- 根据发出的结果打印适当的消息。
运行playground,得到结果:
Couldn't find a match for the criteria
将原来的
.contains(where: { $0.id == 800 })
改为
.contains(where: { $0.id == 800 || $0.name == "Marin Todorov" })
再次运行playground,得到结果:
Criteria matches!
allSatisfy
发布一个布尔值,指示是否所有接收到的元素都通过给定的闭包中的条件。
它是贪婪的,因此会等到上游发布者发出 .finished 完成事件。
在playground中加入如下代码:
let targetRange = (-1...100)
let numbers = [-1, 0, 10, 5]
numbers.publisher
.allSatisfy { targetRange.contains($0) }
.sink { print("\($0)") }
// Prints: "true"
使用 allSatisfy(_:) 运算符来确定流中的所有元素是否满足您提供的条件。当此发布者收到一个元素时,它会针对该元素在闭包中判断。如果返回 false,则发布者生成 false 并结束。如果上游发布者正常完成,则此发布者产生一个true并完成。
reduce
提供一个闭包,用于收集流的每个元素并在上游publisher发布.finished后得出最终结果。
闭包接收所有元素,并在上游发布者完成时发布累计处理的值(如累加)。如果reduce(::) 收到上游发布者的错误,reduce将其传递给下游订阅者,发布者将终止并且不发布任何值。
在playground中加入如下代码:
let numbers = (0...10)
cancellable = numbers.publisher
.reduce(0, { accum, next in accum + next })
.sink { print("\($0)") }
运行playground,得到结果:
55
在这个示例中,reduce(::) 运算符收集从其上游发布者接收的所有整数值并计算累加结果。
注意,这里可以简化代码写法,将reduce()行改为
.reduce("", +)
第三章中的scan和reduce很相似,主要区别在于 scan 为每个发出的值发出累积值,而一旦上游发布者发送 .finished 完成事件,reduce 就会发出单个累积值。随意将上面的例子中的reduce更改为scan,然后自己尝试一下。
本章key points
- 您可以使用 min 和 max 分别发出发布者发出的最小值或最大值。
- first、last 和 output(at:) 在您想查找在特定索引处发出的值时很有用。使用 output(in:) 查找在索引范围内发出的值。
- first(where:) 和 last(where:) 每个都使用一个谓词来确定它应该让哪些值通过。
- count、contains 和 allSatisfy 等运算符不会发出发布者发出的值。相反,它们会根据发出的值发出不同的值。
- contains(where:) 使用谓词来确定发布者是否包含给定的值。
- 使用 reduce 将发出的所有值累积为单个值