在广义上来说,对于一个pipline,我们想要的数学运算也就只有3部分,求数据集合中的最大值,最小值或者个数,至于其他更微小精准的控制,可以使用其他Operators。
max
.max()会等待上游publisher发送.finished事件后,把数据流中的最大值发送出去,凡是涉及到计算极值的问题,被计算的数据都必须实现Comparable协议,因为只有实现了这个协议,才能比较两个值的大小。
_ = [1, 2, 3, 4]
.publisher
.max()
.sink(receiveCompletion: { completion in
...
}, receiveValue: { someValue in
print("someValue: \(someValue)")
})
然而在真实的开发环境中,数据流中的数据很可能是各种各样的类型,不是所有数据都实现了Comparable协议,这种情况应该怎么办呢?
比如下边这种情况:
struct Student {
let age: Int
let name: String
}
let students = [Student(age: 15, name: "张三"),
Student(age: 16, name: "李四"),
Student(age: 17, name: "王五")]
Student并没有实现Comparable协议,未解决此问题,max提供了一个额外参数,它是一个闭包,可以传入自定义的排序策略。其定义如下:
public func max(by areInIncreasingOrder: (Publishers.Sequence<Elements, Failure>.Output, Publishers.Sequence<Elements, Failure>.Output) -> Bool) -> Optional<Publishers.Sequence<Elements, Failure>.Output>.Publisher
重点是areInIncreasingOrder,它要求闭包的结果体现升序的概念,因此,代码如下:
.max { v1, v2 -> Bool in
v1.age < v2.age
}
完整代码如下:
_ = students
.publisher
.max { v1, v2 -> Bool in
v1.age < v2.age
}
.sink(receiveCompletion: { completion in
...
}, receiveValue: { someValue in
print("someValue: \(someValue)")
})
示意图:
在Combine中,凡是使用闭包的Operator都有可能会抛出异常,max也不例外。比如,当发现某个student的age==0时,我们抛出异常,代码如下:
enum MyCustomError: Error {
case customError
}
let students1 = [Student(age: 15, name: "张三"),
Student(age: 16, name: "李四"),
Student(age: 0, name: "王五")]
_ = students1
.publisher
.tryMax { v1, v2 -> Bool in
if v1.age == 0 || v2.age == 0 {
throw MyCustomError.customError
}
return v1.age < v2.age
}
.sink(receiveCompletion: { completion in
print("结束了")
switch completion {
case .finished:
print("完成")
case .failure(let error):
print("错误:\(error.localizedDescription)")
}
}, receiveValue: { someValue in
print("someValue: \(someValue)")
})
一旦抛出异常,pipline就会结束,sink收到.failure事件。示意图如下:
min
min和max完全相反,用于计算数据流中的最小值,我们就不做更多解释了,具体代码如下:
.min()
struct Student {
let age: Int
let name: String
}
let students = [Student(age: 15, name: "张三"),
Student(age: 16, name: "李四"),
Student(age: 17, name: "王五")]
.min { v1, v2 -> Bool in
v1.age < v2.age
}
.tryMin { v1, v2 -> Bool in
if v1.age == 0 || v2.age == 0 {
throw MyCustomError.customError
}
return v1.age < v2.age
}
count
count很好理解,它等待上游publisher发送.finished事件后,返回数据的个数,在某种场景下,当我们需要统计数据的个数时使用该Operator,就没必要把数据保存到数组中,再计算个数这么复杂的操作了。
.count()