Apple原生Rx框架Combine简介

2019-12-22  本文已影响0人  zzzworm

Combine是什么

a declarative Swift API for processing values over time.

Combine是苹果推出的函数式Rective编程框架,和RxSwift,ReactiveObjC类似,主要用于处理时间变化的数据或者事件流。Apple的SDK中Combine是一个独立的Framework但是在许多其他的库中都有Combine的支持,比如SwiftUI就大量应用了Combine,其他的一些基础功能比如,NSNotificationCenter,URLSession和Timer也都有Combine的支持

Combine基本概念

函数用来返回一个值,Combine返回可能的多个值(基于时间序列)
函数返回错误或者抛出异常,Combine返回失败
Combine中有两种基本返回,正常输出和失败

Publisher,Subscriber

发布者(Publisher)的角色就是提供输出(output),当有值或者被请求就输出值,如果一个Publisher没有任何请求则被优化不做任何处理,Combine中提供两种基本输出,正常输出(output type)和失败(Failure)
与发布者(Publisher)对应的是订阅者(Subscriber),订阅者(Subscriber)订阅发布者的输出并进行处理。发布者(Publisher)和订阅者(Subcriber)构成Combine的核心概念。和Publisher对应的,Subscriber有一个输入类型(Input Type)和失败(Failure),关系如下图:

input_output.png

仅仅是Publisher和Subcriber还不够,我们可能需要对数据进行一定的处理再交给下一个,这就引入了另外一个概念,operator,operator同时支持了publisher和Subcriber协议,通过operator你可以订阅一个Publisher,接受它的输出处理后重新发布给其他Subcriber,组合起来就是这样:


image.png

操作者通常用来做数据类型变换,同时作用于输出类型和失败类型,操作者也可以用于分割输出,复制输出或者组合多个流。操作者的一个限制是输入和输出连接需要对齐,就像一个管道一样,所有连接处都必须一致。我们称这种组合方式为管道(Pipeline)

一个简单的示例:

import Combine
let _ = Just(5) (1)
.map { value -> String in  (2)
    // do something with the incoming value here
    // and return a string
    return "a string"
}
.sink { receivedValue in (3)
    // sink is the subscriber and terminates the pipeline
    print("The end result was \(receivedValue)")
}

(1) 管道起始于发布者Just,输出是5 <integer>类型,失败类型是<Never>
(2) 管道通过了map操作,返回了一个字符串,输出类型为<String>
(3) subcriber订阅了map后的输出,并打印了result。

发布者和订阅者的生命周期

Combine的设计是让终端操作可以完全控制数据流和管道处理流程,这是一种 back-pressure设计(可以理解为末端控制整个流程),这也意味着subscriber驱动订阅或者operator应该怎么输出数据。subscriber的请求通过通道(Pipeline)逐级向上链条传递。一个典型的例子就是cancel,当Subcriber要求取消的时候可中断链条上的所有操作。

image.png

也就是subscriber控制整个链条的生命周期

典型的Publisher

Operators

     let pub = (0...5)
         .publisher
         .scan(0, { return $0 + $1 })
         .sink(receiveValue: { print ("\($0)", terminator: " ") })
      // Prints "0 1 3 6 10 15 ".
[(x: 2, y: 3), (x: 1, y: 5), (x: 2, y: 6)].publisher
    .map(\.x, \.y)
    .sink(receiveCompletion: { print($0) },
        receiveValue: { x, y in print(x + y) })
    .store(in: &subscriptions)
5
6
8
finished
.flatMap { data in
    return Just(data)
    .decode(YourType.self, JSONDecoder())
    .catch {
        return Just(YourType.placeholder)
    }
}

flatmap常用于异常处理,在我们处理信号时,有时候部分信号代表错误,而你希望做catch处理后给一个默认值就很有用了,像上面一样。

[1, 2, 3].publisher
    .reduce(0, +)
    .sink(receiveCompletion: { print($0) },
        receiveValue: { print($0) })
    .store(in: &subscriptions)

6
finished
[1, 2, 3].publisher
    .filter { $0 < 2 }
    .sink(receiveCompletion: { print($0) },
        receiveValue: { print($0) })
    .store(in: &subscriptions)

1
finished

类似的还有fist,last,drop,dropFirst, prefix

Subscriber

Combine 内置的 Subscriber 有三种:

let once: Publishers.Once<Int, Never> = Publishers.Once(100)
let observer: Subscribers.Sink<Publishers.Once<Int, Never>> = Subscribers.Sink(receiveCompletion: {
    print("completed: \($0)")
}, receiveValue: {
    print("received value: \($0)")
})
once.subscribe(observer)

// received value: 100
// completed: finished
class Student {
    let name: String
    var score: Int

    init(name: String, score: Int) {
        self.name = name
        self.score = score
    }
}

let student = Student(name: "Jack", score: 90)
print(student.score)
let observer = Subscribers.Assign(object: student, keyPath: \Student.score)
let publisher = PassthroughSubject<Int, Never>()
publisher.subscribe(observer)
publisher.send(91)
print(student.score)
publisher.send(100)
print(student.score)

// 90
// 91
// 100

一单Publisher发送新的值,Student的score属性也会随着发生变化。

// Before
class ContentManager {

    var content: [String] {
        didSet {
            delegate?.contentDidChange(content)
        }
    }

    func changeContent() {
        content = ["hello", "world"]
    }
}

// After
class RxContentController {

    var content = CurrentValueSubject<[String], NSError>([])

    func changeContent() {
        content.value = ["hello", "world"]
    }
}

PassthroughSubject 和 CurrentValueSubject 几乎一样,只是没有初始值,也不会保存任何值。

上一篇 下一篇

猜你喜欢

热点阅读