0Pricing
Swift Academy · 课时

自定义发布者与订阅者

构建您自己的 Combine 组件。

自定义发布者与订阅者 是 CoddyKit 上的免费 Swift Academy 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Swift Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Swift Academy 课程共包含 4 节课。

超越内置发布者

Combine 自带许多发布者,但有时您需要将非 Combine 接口包装成发布者,或者使用精确的背压控制来消费值。这意味着您需要自行实现发布者和订阅者协议。

发布者协议

发布者声明其 Output 和 Failure 类型,并实现 receive(subscriber:),在该方法中将订阅交给传入的订阅者。

protocol Publisher {
    associatedtype Output
    associatedtype Failure: Error
    func receive<S: Subscriber>(subscriber: S)
        where S.Input == Output, S.Failure == Failure
}

订阅的作用

订阅者连接后,发布者会创建一个订阅对象。订阅是管理需求并传递值的连接。它遵循 Subscription,而 Subscription 扩展了 Cancellable。

自定义订阅

订阅会存储订阅者,并通过发出值来响应需求请求。

final class CountSubscription<S: Subscriber>: Subscription
    where S.Input == Int {
    private var subscriber: S?
    private var current = 0

    init(subscriber: S) {
        self.subscriber = subscriber
    }

    func request(_ demand: Subscribers.Demand) {
        var remaining = demand
        while remaining > 0 {
            remaining -= 1
            remaining += subscriber?.receive(current) ?? .none
            current += 1
        }
    }

    func cancel() {
        subscriber = nil
    }
}

自定义发布者

发布者只需创建订阅,然后通过 receive(subscription:) 将其传递给订阅者。

struct CountPublisher: Publisher {
    typealias Output = Int
    typealias Failure = Never

    func receive<S: Subscriber>(subscriber: S)
        where S.Input == Int, S.Failure == Never {
        let subscription = CountSubscription(subscriber: subscriber)
        subscriber.receive(subscription: subscription)
    }
}

了解需求

背压是 Combine 的流控制机制。订阅者请求一定的 Demand,发布者不得发送超出请求数量的值。从 receive(_:) 返回需求会增加允许发送的数量。

订阅者协议

自定义订阅者需要实现三个方法:接收订阅,然后接收每个值,最后接收完成事件。

protocol Subscriber {
    associatedtype Input
    associatedtype Failure: Error
    func receive(subscription: Subscription)
    func receive(_ input: Input) -> Subscribers.Demand
    func receive(completion: Subscribers.Completion<Failure>)
}

自定义订阅者

这里的订阅者会预先请求两个值,然后每接收一个值就再请求一个,同时输出相应信息。

final class PrintSubscriber: Subscriber {
    typealias Input = Int
    typealias Failure = Never

    func receive(subscription: Subscription) {
        subscription.request(.max(2))
    }

    func receive(_ input: Int) -> Subscribers.Demand {
        print("Value:", input)
        return .max(1)
    }

    func receive(completion: Subscribers.Completion<Never>) {
        print("Done")
    }
}

包装委托接口

自定义发布者的一项实际用途,是将委托或回调接口桥接到 Combine 中,从而让旧代码具备组合能力。您可以存储订阅者,并将委托回调转发为 receive(_:) 调用。

可以使用主题时优先使用主题

通常没有必要手动实现这些协议。对于大多数桥接场景,PassthroughSubject 或 CurrentValueSubject 要简单得多——您只需调用 send(_:)。

let subject = PassthroughSubject<Int, Never>()

subject
    .sink { print($0) }
    .store(in: &cancellables)

subject.send(1)
subject.send(2)

何时进行自定义实现

在需要以下功能时,请考虑使用自定义发布者/订阅者实现:

  • 细粒度的背压控制
  • 高效地包装复杂的非 Combine 数据源
  • 可复用且类型安全的运算符

除此之外,主题和内置运算符才是合适的工具。

快速检查

测试您对自定义发布者的掌握程度。

回顾

您已经学习了如何实现 Combine 的核心协议:

  • 发布者为每个订阅者创建一个订阅
  • 订阅遵循需求(背压)
  • 订阅者接收订阅、值和完成事件
  • 对于大多数桥接需求,优先使用主题

理解这些内部机制后,Combine 的工作原理就不再神秘。

常见问题解答

「自定义发布者与订阅者」课时是免费的吗?

是的 — 「自定义发布者与订阅者」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Swift Academy 课程的其余内容,请升级到 CoddyKit PRO。 Swift Academy 课程共包含 4 节课。

「自定义发布者与订阅者」这节课中我会学到什么?

构建您自己的 Combine 组件。 你通过在浏览器中直接运行的动手代码来练习 Swift Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Swift Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Swift Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。

「自定义发布者与订阅者」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Swift Academy 课中编写并运行代码吗?

能。每节 Swift Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 转换和组合流
  2. 调度器与线程
  3. 错误处理运算符
  4. 自定义发布者与订阅者
← 返回 Swift Academy