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.
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?
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.