AnyCancellable置为nil未取消订阅的问题排查
AnyCancellable置nil后未取消订阅的问题分析
An AnyCancellable instance automatically calls cancel() when deinitialized.
根据文档描述,AnyCancellable实例会在销毁时自动调用cancel(),但以下代码运行后却出现了不符合预期的结果:
var cancellable: AnyCancellable? let subject: PassthroughSubject<Int, Never>? = PassthroughSubject<Int, Never>() cancellable = subject?.sink(receiveValue: { print("-> sending to cancellable \($0)") }) print("send 1") subject?.send(1) // documentation states "An AnyCancellable instance automatically calls cancel() when deinitialized." print("cancellable nil'd") cancellable = nil print("send 2") subject?.send(2) print("send 3") subject?.send(3) DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(1)) { print("done") }
运行结果
send 1 -> sending to cancellable 1 cancellable nil'd send 2 -> sending to cancellable 2 send 3 -> sending to cancellable 3 done
可以看到,即使将cancellable置为nil,订阅者依然能接收后续发送的值,而使用Set<AnyCancellable>并清空集合/置nil集合时却能正常停止订阅,甚至将代码放入autoreleasepool也无效。
问题原因
这是Playground的特殊内存管理机制导致的。Playground为了方便调试,会自动保留所有创建过的对象引用,直到整个Playground执行周期结束——哪怕你已经将变量置为nil,对应的AnyCancellable实例也不会立刻被销毁,自然无法触发cancel()方法。
验证与解决
如果把这段代码放到普通iOS项目(UIKit/SwiftUI)的生命周期方法中运行,当cancellable = nil执行后,AnyCancellable会立即被销毁,订阅也会随之取消,不会再接收到后续事件。
如果一定要在Playground里验证AnyCancellable的自动取消特性,可以将代码放入局部作用域(比如函数)中,当函数执行完毕,局部作用域内的变量会被销毁,此时AnyCancellable才会被释放并触发cancel():
let subject = PassthroughSubject<Int, Never>() func testCancellable() { var cancellable: AnyCancellable? cancellable = subject.sink(receiveValue: { print("-> sending to cancellable \($0)") }) print("send 1") subject.send(1) print("cancellable nil'd") cancellable = nil } testCancellable() print("send 2") subject.send(2) // 此时不会输出send 2的接收日志,因为cancellable已被销毁并取消订阅
内容的提问来源于stack exchange,提问作者Yogurt
相关产品推荐
相关产品推荐

