Reputation: 3
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?
Upvotes: 0
Views: 107
Reputation: 437632
You asked:
When
self
is defined withweak self
and then assigned to a variable using optional binding in aguard
statement, why is theweak
reference upgraded to a strong reference?
That Swift evolution proposal, SE-0079, from which you got that code snippet, explains what is going on in the two paragraphs immediately following that code snippet:
When it comes time to execute this closure, the
guard
statement effectively asks the question, “Is the view controller represented byself
still alive?” If the answer is no, theguard
forces areturn
and the rest of the closure does not execute.If
self
is still alive, then the weakly-capturedself
will be non-nil
and it will be converted into a strong reference held bystrongSelf
for the duration of the closure’s execution.
So why is this? Consider what “optional binding” does:
You use optional binding to find out whether an optional contains a value, and if so, to make that value available as a temporary constant or variable. Optional binding can be used with
if
,guard
, andwhile
statements to check for a value inside an optional, and to extract that value into a constant or variable, as part of a single action.
In short, “optional binding” will, if the optional has a value, will create a non-optional rendition that you can use within the specified scope. For reference types (like self
), this non-optional is, by definition, a local strong reference. As The Swift Programming Language: Automatic Reference Counting says:
To make this possible, whenever you assign a class instance to a property, constant, or variable, that property, constant, or variable makes a strong reference to the instance. The reference is called a “strong” reference because it keeps a firm hold on that instance, and doesn’t allow it to be deallocated for as long as that strong reference remains.
So, to recap, the guard let strongSelf = self else { return }
will return
if the weak
reference to self
was nil
, but if not, it will create a non-optional variable that references self
, which by definition, is a strong reference that is maintained for the scope of the strongSelf
variable.
You said:
I believe the
guard
statement is simply checking ifself
isnil
.
No, the use of guard let
does more than “simply checking if self
is nil
.” It is checking if it is nil
, and if not, it is also creating a non-optional variable that establishes that strong reference to self
.
As an aside, SE-0079 permits guard let self = self else {…}
syntax. And SE-0345 allows us to shorten that further to just guard let self else {…}
.
Upvotes: 0
Reputation: 13980
To explain it in real simple way,
self
using [weak self]
to avoid retain cycles.self
may be evicted at any moment before or during the callback execution.self
as optional, and start every statement with self?.<...>
, which may be inconvenient, and also (like in your sample code), the condition (which doesn't depend on self) is checked for nothing if self
is in process of being evicted.self
was not evicted before the start of this callback, I don't want it to be evicted until the callback is done".guard let strongSelf = self else { return }
.guard let self else { return }
Note that, while in your example it's justified, a common pitfall of the beginners is using [weak self]
(or weak
in general) too much, where it's not needed. I recommend this article to give you a general idea on when to use it, and where you don't need it (the article is a bit outdated by now, but still relevant).
Upvotes: 0
Reputation: 535306
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.
Upvotes: 1