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

时间:2017-07-24 15:01:18

标签: swift concurrency automatic-ref-counting deinit

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

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永远不应成为问题

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

  

exc_breakpoint(code = exc_i386_bpt subcode = 0x0)

调试器显示self.b完全正常:不是nil,所有值都是应该的。

但是,调试器还显示,同时主线程正在忙取消初始化相应的B;它已在<... *>暂停,即在foo的引用可以从a中删除之前。所以我觉得self.b在这个时候会是一个不好的参考。

这似乎是不幸的时间 - 但我怎么能消除这种崩溃的可能性呢?毕竟,我不能阻止对bar()的异步调用!

1 个答案:

答案 0 :(得分:0)

基本上,我们在此处打破了unowned的前提条件:即使调试器没有显示它,Foo.b 可以成为nil Foo的生命周期。当我们声称(通过使用unowned)时,编译器认为我们不能这样做,所以我们崩溃了。

似乎有两种出路。

  1. 确保Foo.b是包含对Foo相应实例的强引用的最后一个对象。然后,应该删除两个对象&#34;一起&#34;,resp。在Foo.bar()被取消初始化(或之后)时,无法调用Foo.b

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