如何在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:
- 内部保留private的PassthroughSubject用于发送事件
- 对外暴露一个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
相关产品推荐
相关产品推荐

