Accessing unowned reference in deinit

If I access unowned reference of the same instance in deinit, the runtime crashes with

Fatal error: Attempted to read an unowned reference...

See the example below

class A {
    private unowned var this: A?
    private var foo: String?
    init() {
        self.foo = "foo"
        self.this = self
    }

    deinit {
        print("deinit")
        print("\(String(describing: self.foo))")
        print("\(String(describing: self.this))") // A shouldn't be deallocated yet??
    }
}

var a: A? = A()
a = nil

Is this expected? Can someone explain me what is going on in here?

I hit this when using an in-house Observable library. Its usage looks something like this:

class Test : NSObject {
    var foo: Observable<String> = Observable("foo")
    let bar: String = "bar"

    override init() {
        super.init()
        self.foo.addObserver { [unowned self] (val) in
            print("new val \(val) \(self.bar)")
        }
    }

    deinit {
        print("deinit Test")
        self.foo.removeAllObservers()
        print("deinit Test finsihed")
    }
}

Now if someone has changes value of foo at the same time we are deallocating Test this will crash in line print("new val \(val) \(self.bar)") I also verified it is not race condition since "deinit Test finished" is not printed before the crash.

When you get to the deinit block, the corresponding object is no longer alive (the reference count is already at zero).

Using an unowned reference is like asserting that the object is alive, and in the deinit block that is false. Incidentally, if you had a weak reference there, it'd be nil. It looks like in this case a weak capture would be a better approach.

2 Likes

When you are reading an unowned (or weak) reference, you're temporarily moving the value to a retained reference that prevents the object from being deinitialized.

If your object is currently being deinitialized, that means it can't be retained anymore. Making it otherwise would allow retained references to deinitialized objects to exist and would break memory safety.

You might be able to use unowned(unsafe). But if you do be sure you know what you are doing or it might result in corrupted memory.

1 Like