What guarantees does Swift provide regarding weak references?

Are the guarantees that Swift offers about weak references spelled out somewhere official? If not, could someone on the core team tell me in this thread what is guaranteed?

For example, consider this code:

class Foo {
    var delegate: Delegate?
    weak var _delegate: Delegate?
    
    func example() {
        // Let's imagine that at this point `delegate` 
        // is the only strong reference to the instance
        delegate = nil

        if let _delegate {
            EscapeHatch.retain(_delegate)
        }
    }
}

If at the moment of discarding my strong reference I know that it is the last strong reference to the instance, is there a slim chance that immediately after discarding it my weak reference will still reference the object? Or do I have a guarantee that my object will be destroyed?

2 Likes

Not quite an answer, but practically speaking I'd put a precondition(_delegate == nil) and "know" the practical answer that way: either it sits there in the code base for years and never fires, or I will start getting crash reports about it on some platform / swift versions / etc, and only then start acting upon it. Otherwise - no, I don't think there's a formal guarantee and in theory the actual release could happen, say only upon reaching the closing brace in some future compiler version.

From the point of view of the runtime[1], the entire reference count word is updated atomically and deinitialization happens synchronously, so once the final swift_release has finished executing, deinit will have been run and weak references will evaluate to nil.

I don’t think we make any promises about when the underlying memory will be freed, and there’s been various schemes for that over the years with different tradeoffs.


  1. that is, excluding things like “the ARC optimizer moved the last release call somewhere else”, which I don’t think is likely but isn’t my area of expertise ↩︎

1 Like