【发布时间】:2021-11-05 01:38:59
【问题描述】:
我为一些使用委托的旧代码编写了一个 Combine Publisher 包装类。
TLDR;有人可以改进我如何管理自定义发布者的生命周期。最好让它表现得像普通的发布者,在那里你可以沉迷于它而不必担心保留那个实例。
详情 我遇到了一个问题,我必须保留对我的 Publisher 包装器的引用才能使其工作。自定义发布者的每个示例都没有此要求,尽管他们的发布者是结构并且与我的完全不同。
这是我遇到的问题的简化版本。注意 doSomething() 中被注释掉的部分
import Foundation
import Combine
// Old code that uses delegate
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)
}
}
}
// Combine Publisher Wrapper
class PublisherWrapper: Publisher {
typealias Output = Int
typealias Failure = Error
private let subject = PassthroughSubject<Int, Failure>()
var thing: Thing
init(thing: Thing) {
Swift.print("wrapper init")
self.thing = thing
self.thing.delegate = self
}
deinit {
Swift.print("☠️☠️☠️☠️☠️☠️ wrapper deinit")
}
func receive<S>(subscriber: S) where S : Subscriber, Failure == S.Failure, Int == S.Input {
self.subject.subscribe(subscriber)
self.thing.start()
}
}
extension PublisherWrapper: ThingDelegate {
func delegateCall(number: Int) {
Swift.print("publisher delegate call: \(number)")
self.subject.send(number)
self.subject.send(completion: .finished)
}
}
class Test {
var cancellables = Set<AnyCancellable>()
var wrapper: PublisherWrapper?
func doSomething() {
Swift.print("doSomething()")
let thing = Thing()
let wrapper = PublisherWrapper(thing: thing)
self.wrapper = wrapper
// Take a look over here
//
// if you comment out the line above where I set self.wrapper = wrapper
// it prints out the following
//
//start
//doSomething()
//thing init
//wrapper init
//Thing.start()
//☠️☠️☠️☠️☠️☠️ wrapper deinit
//☠️☠️☠️☠️☠️☠️ thing deinit
//
// But if you uncomment the line and retain it and you'll get the following
//start
//doSomething()
//thing init
//wrapper init
//Thing.start()
//publisher delegate call: 99
//value: 99
//finished
//release wrapper: nil
//☠️☠️☠️☠️☠️☠️ wrapper deinit
//☠️☠️☠️☠️☠️☠️ thing deinit
// we get the value and everything works as it should
wrapper.sink { [weak self] completion in
print(completion)
self?.wrapper = nil
print("release wrapper: \(self?.wrapper)")
} receiveValue: {
print("value: \($0)")
}.store(in: &self.cancellables)
}
}
print("start")
let t = Test()
t.doSomething()
有没有一种方法可以避免像这样保留发布商?我问是因为这在使用 flatMap 时会变得非常难看。
【问题讨论】:
-
你的包装没有泄漏。您调用了错误的
print方法。致电Swift.print -
我敢打赌,如果你评论
DispatchQueue.main.async代表会被调用。我认为当你的包装器没有保留在类中时,它会在 doSomething() 退出时被释放。 -
@vladimr vlasov,我试过了,不幸的是它不起作用。
-
@rob 你说得对,它需要 Swift.print 来帮助打印。