You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Combine中使用另一个发布者重发PassthroughSubject事件

问题根因

你尝试的实现逻辑完全搞反了发布者和订阅者的绑定方向:

  • receive(subscriber:) 方法的语义是当前发布者接收传入的订阅者,也就是让这个订阅者订阅自己
  • 你写的rePublisher.receive(subscriber: subscriber)是让你创建的subscriber去订阅rePublisher,相当于监听rePublisher的事件,完全没有把initialPublisher的事件转发到rePublisher的逻辑,所以打印语句永远不会触发。
可直接运行的修正方案

PassthroughSubject本身已经遵守了Subscriber协议,你完全不需要自己封装AnySubscriber,仅需要一行代码即可完成事件转发,不需要sink闭包:

// 把你原来写的两行Republishing相关代码替换成这一行即可
initialPublisher
    .subscribe(rePublisher)
    .store(in: &cancellableSubscriber)

完整修正后的代码如下:

import UIKit
import PlaygroundSupport
import Combine

class MyViewController : UIViewController {

    // MARK: - Observables
    let initialPublisher: PassthroughSubject<Void, Never> = .init()
    let rePublisher: PassthroughSubject<Void, Never> = .init()

    // MARK: - Observer
    private var cancellableSubscriber = Set<AnyCancellable>()

    override func loadView() {
        // MARK: - View Setup
        let view = UIView()
        let button = UIButton(type: .system)
        button.frame = CGRect(x: 100, y: 100, width: 200, height: 20)
        button.setTitle("Button", for: .normal)
        button.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
        view.addSubview(button)
        self.view = view

        // MARK: - Subscriptions
        // 直接绑定转发,无sink闭包
        initialPublisher
            .subscribe(rePublisher)
            .store(in: &cancellableSubscriber)

        // 监听重发的事件
        rePublisher
            .sink {
                print("Received!")
            }
            .store(in: &cancellableSubscriber)
    }

    @objc private func buttonAction() {
        self.initialPublisher.send()
    }
}
PlaygroundPage.current.liveView = MyViewController()
更优雅的优化方案

如果你只是需要对外暴露事件监听能力,不需要对转发的事件做额外处理,完全可以省略额外的rePublisher:

  1. 内部保留private的PassthroughSubject用于发送事件
  2. 对外暴露一个eraseToAnyPublisher()的属性供外部订阅
    实现示例:
class MyViewController : UIViewController {
    // 内部私有的发送对象
    private let initialPublisher: PassthroughSubject<Void, Never> = .init()
    // 对外暴露的只读发布者,外部只能订阅不能发送
    public var eventPublisher: AnyPublisher<Void, Never> {
        initialPublisher.eraseToAnyPublisher()
    }

    private var cancellableSubscriber = Set<AnyCancellable>()

    override func loadView() {
        // 视图搭建逻辑和按钮点击逻辑不变
        let view = UIView()
        let button = UIButton(type: .system)
        button.frame = CGRect(x: 100, y: 100, width: 200, height: 20)
        button.setTitle("Button", for: .normal)
        button.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
        view.addSubview(button)
        self.view = view

        // 外部直接订阅eventPublisher即可,不需要中间转发层
        eventPublisher
            .sink {
                print("Received!")
            }
            .store(in: &cancellableSubscriber)
    }

    @objc private func buttonAction() {
        self.initialPublisher.send()
    }
}

如果确实需要把多个源的事件合并到同一个发布者,直接把所有源Publisher都subscribe到同一个目标Subject即可,不需要为每个源单独写sink转发逻辑。

内容的提问来源于stack exchange,提问作者Marco Boerner

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 20:45:07