You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 01:48:38