swiftclosuresweak-referencesstrong-references

Why does the weak reference upgrade to a strong reference when self defined with weak self is checked for nil in a guard statement?


Question: When self is defined with weak self and then assigned to a variable using optional binding in a guard statement, why is the weak reference upgraded to a strong reference?

I am a beginner with about a month of experience in Swift.

Recently, I came across the following common piece of code:

// refs: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0079-upgrade-self-from-weak-to-strong.md#introduction
networkRequest.fetchData() { [weak self] result in
    guard let strongSelf = self else { return }

    switch result {
    case .Succeeded(let data):
        strongSelf.processData(data)

    case .Failed(let err):
        strongSelf.handleError(err)
    }
}

In this code, self is defined as a weak reference, and the value (a reference to self?) is assigned to strongSelf using optional binding in a guard statement.

This raised a question for me: why is self, which was defined with weak self, being assigned to a variable like strongSelf, which seems to be a strong reference?

While researching this question, I found a proposal related to this topic that explained the concept with the following quote:

If self is still alive, then the weakly-captured self will be non-nil and it will be converted into a strong reference held by strongSelf for the duration of the closure’s execution.

Source: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0079-upgrade-self-from-weak-to-strong.md#introduction

I don’t think the proposal is incorrect, but I believe the guard statement is simply checking if self is nil. Therefore, I didn’t quite understand what it means for the weak reference to be upgraded to a strong reference.

I would appreciate it if you could explain this.

The documents I referred to are as follows:

None of these documents explicitly mention that a weak reference is upgraded to a strong reference after a guard statement, which made me curious.

I would appreciate it if you could explain this.

Similar questions: Could a guard let `self` = self inside a closure that uses [weak self] cause a crash if the object they reference becomes deallocated?


Solution

  • You seem to think that the strength or weakness belongs to the value of the variable. It does not. It belongs to the reference (i.e., the name of the variable). If, say, self is a view controller, it is not a "weak view controller" just because you say [weak self]; no such thing exists. Rather, the name self is a weak reference to the view controller.

    But strongSelf is not declared as weak, i.e. you didn't say

    weak var strongSelf = ...
    

    Therefore it is strong. So now we have a strong reference to the view controller, if we can get a reference at all.

    How do we get that reference? Well, if let and guard let unwrap and assign. The [weak self] specification causes a weak and therefore Optional reference to arrive into the block, i.e. self?. If the view controller (or whatever it is) has gone out of scope, this is nil. If not, you can refer to it as self? within the block. But to obviate the need for repeated references to self?, as well as the possibility that the referent might go out of scope partway thru the block, we may as well retain it if it is not nil — which is exactly what if let and guard let do.

    Finally, note that your code is way outdated; you should now just say

    guard let self else { return }
    

    This is short for

    guard let self = self else
    

    which itself is short for

    guard let `self` = self
    

    In other words, we are unwrapping the weak reference self and assigning the resulting value (the view controller or whatever) to a different reference self which is strong because we didn't declare it as weak.