自定义Combine Publisher包装类需手动保留才能运行,如何优化其生命周期管理?
问题根源
你遇到的问题本质是 Combine 的订阅链默认不会持有 Publisher 本身,你把状态(Thing 实例、代理实现)放在了作为类的 Publisher 里,作为局部变量的 Publisher 出了作用域就会被销毁,导致异步回调无法触发。
Combine 标准设计范式是:Publisher 是轻量的描述值生成规则的值类型(结构体),不持有运行时状态,所有状态都托管到被订阅链持有的 Subscription 实例中,只要你把 sink 返回的 Cancellable 存入持有集合,Subscription 就会一直存活直到订阅结束,不需要手动持有 Publisher。
解决方案
下面是符合 Combine 设计规范的修改后完整代码,无需手动持有 Publisher 即可正常运行:
import Foundation import Combine // 原有旧代码无需修改 protocol ThingDelegate: AnyObject { func delegateCall(number: Int) } class Thing { weak var delegate: ThingDelegate? var name: String = "Stuff" init() { Swift.print("thing init") } deinit { Swift.print("☠️☠️☠️☠️☠️☠️ thing deinit") } func start() { Swift.print("Thing.start()") DispatchQueue.main.async { self.delegate?.delegateCall(number: 99) } } } // 修改为结构体类型的Publisher struct PublisherWrapper: Publisher { typealias Output = Int typealias Failure = Error private let thing: Thing init(thing: Thing) { self.thing = thing Swift.print("wrapper init") } func receive<S>(subscriber: S) where S : Subscriber, Failure == S.Failure, Int == S.Input { // 所有状态都放到Subscription中,订阅链会自动持有Subscription实例 let subscription = InnerSubscription( subscriber: subscriber, thing: thing ) subscriber.receive(subscription: subscription) } } // 自定义Subscription,持有所有运行时状态 private class InnerSubscription<S: Subscriber>: Subscription, ThingDelegate where S.Input == Int, S.Failure == Error { private var subscriber: S? private let thing: Thing private let subject = PassthroughSubject<Int, Error>() private var cancellable: AnyCancellable? init(subscriber: S, thing: Thing) { self.subscriber = subscriber self.thing = thing self.thing.delegate = self // 绑定subject和订阅者 self.cancellable = subject.subscribe(subscriber) self.thing.start() } func request(_ demand: Subscribers.Demand) {} func cancel() { // 订阅取消时清理资源 cancellable = nil subscriber = nil thing.delegate = nil } // 实现代理协议 func delegateCall(number: Int) { Swift.print("publisher delegate call: \(number)") self.subject.send(number) self.subject.send(completion: .finished) // 完成后自动清理 cancel() } deinit { Swift.print("☠️☠️☠️☠️☠️☠️ subscription deinit") } } // 测试代码 class Test { var cancellables = Set<AnyCancellable>() // 无需再声明持有wrapper的属性 func doSomething() { Swift.print("doSomething()") let thing = Thing() let wrapper = PublisherWrapper(thing: thing) // 不需要手动持有wrapper wrapper.sink { completion in print(completion) print("订阅结束") } receiveValue: { print("value: \($0)") }.store(in: &self.cancellables) } } print("start") let t = Test() t.doSomething()
效果说明
注释掉手动持有 Publisher 的代码后,依然可以正常收到回调值和完成事件,也能正常触发所有实例的析构方法,flatMap 等操作符可以直接使用,无需额外处理。
轻量简化方案
如果不想编写自定义 Subscription,也可以用 Deferred 闭包捕获状态的方式实现,代码更短:
func thingPublisher(thing: Thing) -> AnyPublisher<Int, Error> { Deferred { let subject = PassthroughSubject<Int, Error>() let proxy = DelegateProxy(subject: subject) thing.delegate = proxy thing.start() return subject .handleEvents(receiveCancel: { thing.delegate = nil _ = proxy // 强制持有proxy直到订阅结束 }) } .eraseToAnyPublisher() } private class DelegateProxy: ThingDelegate { let subject: PassthroughSubject<Int, Error> init(subject: PassthroughSubject<Int, Error>) { self.subject = subject } func delegateCall(number: Int) { subject.send(number) subject.send(completion: .finished) } }
内容的提问来源于stack exchange,提问作者Yogurt
相关产品推荐
相关产品推荐

