Swift Combine — Publisher、Operator、Subscriber概念介绍

Combine框架介绍

Combine框架提供了一个声明式Swift API,用于处理随时间变化的值。这些值可以表示多种异步事件。Combine声明发布者公开发布可能随时间变化的值,并声明订阅者从发布者接收这些值。

Combine框架为应用程序如何处理事件提供了一种声明式的方法。可以为给定的事件源创建单个处理链,而不是潜在地实现多个委托回调或完成处理闭包。一个事件及其对应的数据被发布出来,最后被订阅者消化和使用,期间这些事件和数据需要通过一系列操作变形,成为我们最终需要的事件和数据。

Combine中最重要的角色有三种,恰好对应了这三种操作:负责发布事件的Publisher,负责订阅事件的Subscriber,以及负责转换事件和数据的Operator

Publisher 发布者

Publisher是一个基础协议,它代表事件的发布者,其他各式各样的Publisher都继承了这个基础Publisher协议。Publisher协议的定义也比较简单,包括两个关联类型(associatedtype)和一个receive方法。

swift 复制代码
public protocol Publisher<Output, Failure> {

    /// The kind of values published by this publisher.
    associatedtype Output

    /// The kind of errors this publisher might publish.
    ///
    /// Use `Never` if this `Publisher` does not publish errors.
    associatedtype Failure : Error

    /// Attaches the specified subscriber to this publisher.
    ///
    /// Implementations of ``Publisher`` must implement this method.
    ///
    /// The provided implementation of ``Publisher/subscribe(_:)-4u8kn``calls this method.
    ///
    /// - Parameter subscriber: The subscriber to attach to this ``Publisher``, after which it can receive values.
    func receive<S>(subscriber: S) where S : Subscriber, Self.Failure == S.Failure, Self.Output == S.Input
}

Publisher 最主要的工作其实有两个:发布新的事件及其数据,以及准备好被Subscriber订阅。
Output 定义了某个 Publisher 所发布的值的类型,Failure 则定义可能产生的错误的类型。随着时间的推移,事件流也会逐渐向前发展。对应 OutputFailurePublisher 可以发布三种事件:

  1. 类型为 Output 的新值:这代表事件流中出现了新的值。
  2. 类型为 Failure 的错误:这代表事件流中发生了问题,事件流到此终止。
  3. 完成事件:表示事件流中所有的元素都已经发布结束,事件流到此终止。

Subscriber介绍

Subscriber 也是一个抽象的协议。

swift 复制代码
public protocol Subscriber<Input, Failure> : CustomCombineIdentifierConvertible {

    /// The kind of values this subscriber receives.
    associatedtype Input

    /// The kind of errors this subscriber might receive.
    ///
    /// Use `Never` if this `Subscriber` cannot receive errors.
    associatedtype Failure : Error

    /// Tells the subscriber that it has successfully subscribed to the publisher and may request items.
    ///
    /// Use the received ``Subscription`` to request items from the publisher.
    /// - Parameter subscription: A subscription that represents the connection between publisher and subscriber.
    func receive(subscription: any Subscription)

    /// Tells the subscriber that the publisher has produced an element.
    ///
    /// - Parameter input: The published element.
    /// - Returns: A `Subscribers.Demand` instance indicating how many more elements the subscriber expects to receive.
    func receive(_ input: Self.Input) -> Subscribers.Demand

    /// Tells the subscriber that the publisher has completed publishing, either normally or with an error.
    ///
    /// - Parameter completion: A ``Subscribers/Completion`` case indicating whether publishing completed normally or with an error.
    func receive(completion: Subscribers.Completion<Self.Failure>)
}

定义中 InputFailure 分别表示了订阅者能够接受的事件流数据类型和错误类型。想要订阅某个 PublisherSubscriber 中的这两个类型必须与 Publisher 的 Output 和 Failure 一致。

Subscribers.Sink是一个简单的订阅者,在订阅时请求无限数量的值。

swift 复制代码
public func sink(receiveCompletion: @escaping ((Subscribers.Completion<Self.Failure>) -> Void), receiveValue: @escaping ((Self.Output) -> Void)) -> AnyCancellable
public func sink(receiveValue: @escaping ((Self.Output) -> Void)) -> AnyCancellable

该方法可以同时提供两个闭包也可以一个,receiveCompletion用来接收 failure 或者 finished 事件,receiveValue 用来接收 output 值。

还有一个 Subscriber 可能会更为简洁常用,那就是 assign。和通过 sink 提供闭包,可以执行任意操作不同,assign 接受一个 class 对象以及对象类型上的某个键路径 (key path)。每当 output 事件到来时,其中包含的值就将被设置到对应的属性上去。

swift 复制代码
public func assign<Root>(to keyPath: ReferenceWritableKeyPath<Root, Self.Output>, on object: Root) -> AnyCancellable

使用这个方法有些要求,只有那些 class 类型的实例中的属性能被绑定,在SwiftUI中,我们厂用的和View匹配的ViewModel继承了 ObservableObject,而继承 ObservableObject的只能是class类,因此assign放在常用在这里。

Operator介绍

Operator提供了一些方法,他们接收上有的元素进行操作,然后创建下游发布者或者订阅者。

使用Operator组装一个Publisher链(可以以订阅者结束),该Publisher链处理上游Publisher生成的元素。每个Operator创建并配置PublisherSubscriber的实例,并将其订阅到调用该方法的Publisher上。

说起来不太好理解,看看下面这个例子。

swift 复制代码
let cancellable = [1, 2, 3, 4, 5].publisher
    .filter {
        $0 % 2 == 0
    }
    .sink {
        print ("Even number: \($0)")
    }
// Prints:
// Even number: 2
// Even number: 4

在上面的示例中,一个数组Publisher发出整数1、2、3、4、5。filter操作符创建了一个Publisher,重新发布偶数值。sink操作符创建Subscriber,该Subscriber打印接收到的每个值。sink创建的Subscriber自定订阅filter创建的Publisherfilter创建的Publisher订阅了数组Publisher

写在最后

PublisherOperatorSubscriber 三者组成了从事件发布,变形,到订阅的完整链条。在建立起事件流的响应链后,随着事件发生,app 的状态随之演变,这些是响应式编程处理异步程序的 Combine 框架的基础架构。

相关推荐
missmisslulu5 小时前
电容笔值得买吗?2024精选盘点推荐五大惊艳平替电容笔!
学习·ios·电脑·平板
GEEKVIP7 小时前
手机使用技巧:8 个 Android 锁屏移除工具 [解锁 Android]
android·macos·ios·智能手机·电脑·手机·iphone
GEEKVIP7 小时前
如何在 Windows 10 上恢复未保存/删除的 Word 文档
macos·ios·智能手机·电脑·word·笔记本电脑·iphone
奇客软件7 小时前
iPhone使用技巧:如何恢复变砖的 iPhone 或 iPad
数码相机·macos·ios·电脑·笔记本电脑·iphone·ipad
奇客软件1 天前
如何从相机的记忆棒(存储卡)中恢复丢失照片
深度学习·数码相机·ios·智能手机·电脑·笔记本电脑·iphone
GEEKVIP1 天前
如何修复变砖的手机并恢复丢失的数据
macos·ios·智能手机·word·手机·笔记本电脑·iphone
一丝晨光2 天前
继承、Lambda、Objective-C和Swift
开发语言·macos·ios·objective-c·swift·继承·lambda
GEEKVIP2 天前
iPhone/iPad技巧:如何解锁锁定的 iPhone 或 iPad
windows·macos·ios·智能手机·笔记本电脑·iphone·ipad
KWMax2 天前
RxSwift系列(二)操作符
ios·swift·rxswift