zztkm
zztkm

Reputation: 3

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?

Upvotes: 0

Views: 107

Answers (3)

Rob
Rob

Reputation: 437632

You asked:

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?

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 by self still alive?” If the answer is no, the guard forces a return and the rest of the closure does not execute.

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.

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, and while 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 if self is nil.

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

timbre timbre
timbre timbre

Reputation: 13980

To explain it in real simple way,

  • In some cases, you must "weakify" self using [weak self] to avoid retain cycles.
  • This means that your self may be evicted at any moment before or during the callback execution.
  • So in the callback, you either need to treat 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.
  • So instead you may say: "if self was not evicted before the start of this callback, I don't want it to be evicted until the callback is done".
  • You do that the same way you convert every variable from weak to strong reference: by assigning it to another strongly-referenced variable, e.g. guard let strongSelf = self else { return }.
  • The more modern syntax, like matt mentioned, is 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

matt
matt

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

Related Questions