当您意识到发布者只是序列本身时,序列操作符最容易理解。序列操作符处理发布者的值,很像数组或集合——当然,它们只是有限序列! 考虑到这一点,序列运算符主要将发布者作为一个整体处理,而不是像其他操作符那样处理单个值。 该类别中的许多运算符与 Swift 标准库中的对应运算符具有几乎相同的名称和行为。
Finding values
根据不同的标准定位发布者发出的特定值。
min
min 操作符可让您找到发布者发出的最小值。它是贪婪的,这意味着它必须等待发布者发送一个 .finished 完成事件。一旦发布者完成,这个操作符只会发布最小值到下游。
在playground中加入如下代码:
// 1
let publisher = [1, -50, 246, 0].publisher
// 2
publisher
.print("publisher")
.min()
.sink(receiveValue: { print("Lowest value is \($0)") })
.store(in: &subscriptions)
运行playground,得到结果:
publisher: receive subscription: ([1, -50, 246, 0])
publisher: request unlimited
publisher: receive value: (1)
publisher: receive value: (-50)
publisher: receive value: (246)
publisher: receive value: (0)
publisher: receive finished
Lowest value is -50
如您所见,发布者发出其所有值并完成,然后 min 找到最小值并将其发送到下游以将其打印出来。 但是等等,Combine 如何知道这些数字中的哪一个是最小值?嗯,这要归功于数值符合 Comparable 协议这一事实。您可以在发出 Comparable-conformed 类型的发布者上直接使用 min() ,无需任何参数。 但是如果您的值不符合 Comparable 会发生什么?幸运的是,您可以使用 min(by:) 运算符提供自己的比较器闭包。 考虑以下示例,其中您的发布者发出许多数据,而您想找到最小的数据
// 1
let publisher = ["12345",
"ab",
"hello world"]
.map { Data($0.utf8) } // [Data]
.publisher // Publisher<Data, Never>
// 2
publisher
.print("publisher")
.min(by: { $0.count < $1.count })
.sink(receiveValue: { data in
// 3
let string = String(data: data, encoding: .utf8)!
print("Smallest data is \(string), \(data.count) bytes")
})
.store(in: &subscriptions)
例子中publisher发布的数据不符合Comparable协议,所以用min(by:)来查找其中的最小值。
运行playground,得到结果:
publisher: receive subscription: ([5 bytes, 2 bytes, 11 bytes])
publisher: request unlimited
publisher: receive value: (5 bytes)
publisher: receive value: (2 bytes)
publisher: receive value: (11 bytes)
publisher: receive finished
Smallest data is ab, 2 bytes
max
和min的原理一致,但功能相反。max找到发布者发布的最大值。
在playground中加入如下代码:
// 1
let publisher = ["A", "F", "Z", "E"].publisher
// 2
publisher
.print("publisher")
.max()
.sink(receiveValue: { print("Highest value is \($0)") })
.store(in: &subscriptions)
运行playground,得到结果:
publisher: receive subscription: (["A", "F", "Z", "E"])
publisher: request unlimited
publisher: receive value: (A)
publisher: receive value: (F)
publisher: receive value: (Z)
publisher: receive value: (E)
publisher: receive finished
Highest value is Z
就像 min 一样,max 是贪婪的,必须等待上游发布者完成其值的发布,然后才能确定最大值。
let publisher = PassthroughSubject<Int, Never>()
publisher
.print("publisher")
.max()
.sink(receiveValue: { print("Max value is \($0)") })
.store(in: &subscriptions)
publisher4.send(1)
publisher4.send(2)
publisher4.send(completion: .finished)
运行playground,得到结果:
publisher4: receive subscription: (PassthroughSubject)
publisher4: request unlimited
publisher4: receive value: (1)
publisher4: receive value: (2)
publisher4: receive finished
Max value is 2
像上面这个例子,如果我们注释掉
publisher4.send(completion: .finished)
则无法在控制台看到"Max value is ..."的打印,因为publisher没有发布完成,所以max(同理min也是一样)无法工作。
first
first让Publisher第一个发出的值通过然后完成。它是惰性的,这意味着它不会等待上游发布者完成,而是在收到第一个发出的值时取消订阅。
在playground中加入如下代码:
// 1
let publisher = ["A", "B", "C"].publisher
// 2
publisher
.print("publisher")
.first()
.sink(receiveValue: { print("First value is \($0)") })
.store(in: &subscriptions)
运行playground,得到结果:
publisher: receive subscription: (["A", "B", "C"])
publisher: request unlimited
publisher: receive value: (A)
publisher: receive cancel
First value is A
可以看出,一旦 first() 获得发布者的第一个值,它就会取消对上游发布者的订阅。
如果您正在寻找更精细的控制,您也可以使用 first(where:)。就像它在 Swift 标准库中的对应函数一样,它将发出与提供的闭包匹配的第一个值。
参考如下代码:
// 1
let publisher = ["J", "O", "H", "N"].publisher
// 2
publisher
.print("publisher")
.first(where: { "Hello World".contains($0) })
.sink(receiveValue: { print("First match is \($0)") })
.store(in: &subscriptions)
运行playground,得到结果:
publisher: receive subscription: (["J", "O", "H", "N"])
publisher: request unlimited
publisher: receive value: (J)
publisher: receive value: (O)
publisher: receive value: (H)
publisher: receive cancel
First match is H
这个demo中我们用first(where:)查找“Hello World”字符串中包含的第一个字符,最终得到结果"H",然后这个订阅就被取消了。
last
last 与 first 完全一样,除了它发出发布者发出的最后一个值。这意味着它是贪婪的,必须等待上游发布者完成。
在playground中加入如下代码:
// 1
let publisher = ["A", "B", "C"].publisher
// 2
publisher
.print("publisher")
.last()
.sink(receiveValue: { print("Last value is \($0)") })
.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
Last value is C
类似max和min这些贪婪操作符,last需要发布者完成发布才能让其执行,所以您可以尝试用PassthroughSubject发布数据,然后不发布completion: .finished状态,观察其结果。这里就不再举例了。
output(at:)
output操作符在指定索引处查找上游发布者发出的值,成功后会取消订阅。 ![[img1526.jpg]]
在playground加入如下代码:
// 1
let publisher = ["A", "B", "C"].publisher
// 2
publisher
.print("publisher")
.output(at: 1)
.sink(receiveValue: { print("Value at index 1 is \($0)") })
.store(in: &subscriptions)
运行playground,得到结果:
publisher: receive subscription: (["A", "B", "C"])
publisher: request unlimited
publisher: receive value: (A)
publisher: request max: (1) (synchronous)
publisher: receive value: (B)
Value at index 1 is B
`publisher: receive cancel`
output(in:)
output(at:) 发出在指定索引处发出的单个值,而 output(in:) 发出索引在提供范围内的所有值,随后取消订阅。
在playground中加入如下代码:
// 1
let publisher = ["A", "B", "C", "D", "E"].publisher
// 2
publisher
.output(in: 1...3)
.sink(receiveCompletion: { print($0) },
receiveValue: { print("Value in range: \($0)") })
.store(in: &subscriptions)
运行playground,得到结果:
Value in range: B
Value in range: C
Value in range: D
`finished`