异步函数调用与无主对象的取消初始化冲突



我有与此类似的代码,与异步上下文中的事件处理有关:

class A {
var foos: Set<Fooable>
}
protocol Fooable {
func bar()
}
class B {
var a: A
var foo: Foo!
init(a: A) {
self.a = a
}
func start() {
self.foo = Foo(self)
self.a.foos.insert(self.foo)
}
deinit {
<... *>
if self.foo != nil {
self.a.remove(self.foo)
}
}

class Foo: Fooable {
unowned let b: B
init(_ b: B) {
self.b = B
}
func bar() { <... #> }
}
}

我认为这应该是安全代码:在b的实例消失之前,它会清理对其foo的所有引用,因此引用Foo.b永远不会成为问题。

但是,我从访问Foo.bar()内部的self.b时收到此错误(在某些 GCD 队列上运行,而不是主队列(:

exc_breakpoint(代码=exc_i386_bpt子代码=0x0(

调试器显示self.b完全没问题:不是 nil,所有值都按原样。

然而,调试器也显示,与此同时,主线程正忙于取消初始化相应的B;它在<... *>中暂停,即在从a中删除对foo的引用之前。因此,在我看来,self.b在这个时间点将是一个糟糕的参考是有道理的。

这似乎是不幸的时机 - 但我如何消除这种崩溃的可能性?毕竟,我无法阻止对bar()的异步调用发生!

基本上,我们在这里打破了unowned的前提条件:即使调试器不显示它,Foo.b也可能Foo的生命周期内变得nil。当我们声称(通过使用unowned(它不能时,编译器相信了我们,所以我们崩溃了。

似乎有两条出路。

  1. 确保Foo.b是最后一个包含对相应实例的强引用的对象Foo。然后,这两个对象应该"一起"删除,因为在取消初始化Foo.b时(或之后(不可能发生对Foo.bar()的调用。

  2. Foo.b作为弱引用,即将其声明为weak var b: B?。这使得代码更加混乱,但至少它可以变得安全。

最新更新