Combine之Operator(Mathematic operations 数学运算符)

1,090 阅读1分钟

github.com/agelessman/…

在广义上来说,对于一个pipline,我们想要的数学运算也就只有3部分,求数据集合中的最大值,最小值或者个数,至于其他更微小精准的控制,可以使用其他Operators。

max

image.png

.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)")
    })

示意图:

image.png

在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事件。示意图如下:

image.png

min

image.png

image.png

image.png

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

image.png

count很好理解,它等待上游publisher发送.finished事件后,返回数据的个数,在某种场景下,当我们需要统计数据的个数时使用该Operator,就没必要把数据保存到数组中,再计算个数这么复杂的操作了。

.count()