0

I have code similar to this, related to event handling in an asynchronous context:

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() { <... #> }
    }
}

I figured that this should be safe code: before an instance of b is gone, it cleans up all references to its foo, so the reference Foo.b should never be an issue.

However, I get this error from an access of self.b inside of Foo.bar() (run on some GCD queue, not main):

exc_breakpoint (code=exc_i386_bpt subcode=0x0)

The debugger shows that self.b is completely fine: not nil, all values are as they should be.

However, the debugger also shows that, at the same time, the main thread is busy deinitializing the corresponding B; it's paused in <... *>, i.e. before the reference to foo could be removed from a. So it makes sense to me that self.b would be a bad reference at this point in time.

This seems to be unfortunate timing -- but how can I do away with this crash potential? I can not prevent asynchronous calls to bar() from happening, after all!

Raphael
  • 9,779
  • 5
  • 63
  • 94

1 Answers1

0

Basically, we are breaking the precondition of unowned here: even though the debugger doesn't show it, Foo.b can become nil during the lifetime of a Foo. The compiler believed us when we claimed (by use of unowned) that it couldn't, so we crash.

There seem to be two ways out.

  1. Make sure that Foo.b is the last object that holds a strong reference to the respective instance of Foo. Then, the two objects should be removed "together", resp. there is no way a call to Foo.bar() can happen while Foo.b is being deinitialized (or after that).

  2. Make Foo.b a weak reference, i.e. declare it as weak var b: B?. That makes the code more messy, but at least it can be made safe.

Raphael
  • 9,779
  • 5
  • 63
  • 94