合并 - 订阅者在第二次订阅时被静默取消

Posted

技术标签:

【中文标题】合并 - 订阅者在第二次订阅时被静默取消【英文标题】:Combine - Subscriber gets cancelled silently when subscribing a second time 【发布时间】:2021-03-17 18:02:08 【问题描述】:

我正在尝试用组合替换委托模式。

我的应用程序的架构是 VIPER,因此我需要将接收器订阅者从一个模块传递到另一个模块。用例是我有来自模块 A(列表)的数据需要显示给模块 B(详细视图),模块 B 也可以更新数据,所以我也需要将其返回到模块 A。

使用委托它可以正常工作,但是当我使用接收器订阅者时,我遇到了问题。

我第一次从模块 A 转到模块 B 时,我传递了订阅者,然后将其订阅给发布者(来自模块 B)它运行良好,模块 A 中的订阅者接收来自模块 B 的所有事件。

但是当关闭模块 B 并再次从 A 路由到 B 时,订阅者在尝试再次订阅时立即收到取消事件:接收订阅:(PublishedSubject)...接收取消...

我做了一个非常简单的例子来说明发生了什么:

模块 A:

class ViewController_A: UIViewController 
    
    var subscriber: AnySubscriber<String, Never>!
    
    override func viewDidLoad() 
        super.viewDidLoad()
        createSubscriber()
    
    
    func createSubscriber() 
        let subscriber = Subscribers.Sink<String, Never>(
            receiveCompletion:  completion in
                print(completion)
            , receiveValue:  value in
                print(value)
            )
        self.subscriber = AnySubscriber(subscriber)
    
    
    func showViewControllerB() 
        let viewControllerB = ViewControllerB()
        viewControllerB.passSubscriber(AnySubscriber(subscriber))
    

模块 B:

protocol MyProtocol 
    var publisher: Published<String>.Publisher  get 
    func passSubscriber(_ subscriber: AnySubscriber<String, Never>)


class ViewController_B: UIViewController, MyProtocol 
    
    @Published var word: String = "House"
    var publisher: Published<String>.Publisher  $word 
    
    func passSubscriber(_ subscriber: AnySubscriber<String, Never>) 
        publisher
            .print()
            .subscribe(subscriber)
    
    
    func dismiss() 
        dismiss(animated: true)
    

路由时..

let viewControllerA = ViewController_A()
viewControllerA.showViewController_B() // When presenting B for the first time, receiving events here
// Dismiss B here...
viewControllerA.showViewControllerB() // When presenting B again (hence subscribing again), the subscription gets cancelled here without receiving any events/values

我注意到一些有趣的事情。当我每次路由到模块 B 时再次创建订阅者,而不是在 viewDidLoad 中只创建一次,它似乎工作正常,但我不知道为什么。

这是否意味着订阅者一旦订阅了另一个发布者,就无法订阅另一个发布者,即使之前的发布者不再存在?

如何使它在我的委托案例中发挥作用?

【问题讨论】:

还有更多你没有给我们看的吗?你的 showViewControllerB 创建一个视图控制器,然后立即销毁它。 顺便说一句,我认为你的分析是非常正确的。您只需在 viewDidLoad 中创建一次订阅者,并且不能重复使用订阅者。每次 A 生成 B 时,您只需重新创建订阅者。 感谢您的回复!是的,很抱歉,这只是一个测试项目来展示正在发生的事情,但真实项目是在 Viper 上制作的,并且模块不会立即被破坏。 哦,我明白了,所以没有办法取消订阅订阅者以订阅新发布者? 没有需要。这些是轻量级对象。您正在两个 real 对象(视图控制器)之间建立通信管道。当通信因为其中一个对象消失而结束时,管道就结束了。现在你有了一个新的 ViewControllerB,创建一个新的管道。 【参考方案1】:

当我每次路由到模块 B 时再次创建订阅者,而不是在 viewdidload 中只创建一次,它似乎工作正常

正确,因为这正是你应该做的。

订阅者已经订阅然后取消/完成是结束,就像发布者已经订阅然后取消/完成是结束。这些是轻量级对象,创建是为了促进两个“真实”端点之间的通信。当其中一个端点结束时,通信结束。

因此,如果您需要新的通信管道,您只需创建一个新的发布者/订阅者即可。

【讨论】:

感谢您的回答!那很有趣。我不明白的是,我从不取消或完成订阅者,那么为什么当我使用新发布者再次订阅它时它会立即取消? 我的印象是,您看到的是 first 管道取消,因为您正试图将其订阅者订阅到另一个管道。但我可能是错的。 谢谢@matt。即使我找不到与该问题相关的任何文档,我也会接受您的回答!无论如何,我都会尝试重新创建它,以确保它不会被取消。谢谢!!

以上是关于合并 - 订阅者在第二次订阅时被静默取消的主要内容,如果未能解决你的问题,请参考以下文章

贝宝订阅,将未结余额添加到订阅按钮的下一个账单

确保在第一个订阅者完成拉入 Google pub-sub 主题后触发第二个订阅者

取消订阅 Rxjs finalize 操作符

Pubsub 推送拉取订阅

在我当前订阅结束之前升级到更高的订阅时,订阅如何工作?

如何取消 useEffect 清理函数中的所有订阅和异步任务?