Combine框架中PassthroughSubject运行无输出是什么原因?
错误原因
你的核心错误是创建订阅后立刻调用了.cancel(),直接终止了订阅关系,后续发送的所有事件都不会被已经取消的订阅者接收。
PassthroughSubject本身不会缓存任何历史事件,只有在订阅处于激活状态时,新发送的事件才会投递给订阅者。你在sink生成订阅的同一行就链式调用.cancel(),订阅刚创建就被立刻销毁,后续执行send("one")、send("two")、send("three")时,已经没有活跃的订阅者存在,自然不会有任何打印输出。
关于另外两个测试用例的说明
你写的序列Publishers、CurrentValueSubject测试代码同样存在立刻取消订阅的问题,只是二者的特性让你误以为代码正常运行:
- 数组等序列类型的
publisher是同步派发事件的:调用sink时,它会在sink方法返回前,就把所有元素、完成事件全部同步发送给订阅者,等链式调用执行到.cancel()时,所有事件已经派发完成,打印动作已经执行完毕,所以你能看到输出。 CurrentValueSubject会始终持有最新的一个值:调用sink订阅它的瞬间,它会先把当前保存的最新值同步推送给订阅者,这个动作同样发生在.cancel()执行之前,所以你能看到打印输出。但如果在调用.cancel()之后再执行send发送新值,一样无法被接收。
正确写法
你需要把sink返回的AnyCancellable订阅实例存储起来,不要立刻调用cancel,订阅会在持有它的实例销毁时自动取消,参考代码:
import UIKit import Combine class DemoViewController: UIViewController { // 用于存储所有订阅,只要当前控制器不被销毁,集合内的订阅就会保持激活 var cancellables = Set<AnyCancellable>() override func viewDidLoad() { super.viewDidLoad() let mySubject = PassthroughSubject<String, Error>() mySubject.sink( receiveCompletion: { completion in print("-- completion", completion) }, receiveValue: { value in print("-- value", value) } ) // 将订阅存入集合,不要立刻调用cancel .store(in: &cancellables) // 此时订阅处于激活状态,发送的事件可以被正常接收 mySubject.send("one") mySubject.send("two") mySubject.send("three") } }
注意:Combine 中所有订阅如果没有被强引用持有、或者被主动调用
cancel,就会被立刻回收终止,不要在创建订阅的链式调用末尾直接写.cancel(),除非你明确需要立刻终止订阅。
内容的提问来源于stack exchange,提问作者Joakim Sjöstedt
相关产品推荐
相关产品推荐

