In Swift, If I Have a Closure Capturing [Weak Self], Is It Good Practice to Unwrap the Optional Self at the Beginning of the Closure

In Swift, if I have a closure capturing [weak self], is it good practice to unwrap the optional self at the beginning of the closure?


I believe that the first implementation is better because self could become nil between the statements

And that's why the second implementation is in fact better! If self is not nil at the first statement, the first statement makes it so that self couldn't become nil between the statements. It retains self exactly for the remainder of the block. This is called "the weak–strong dance".

guard let self = self else { return }
// ^^^^ this self is _strong_, not weak; it retains self

Is it the right way using `[weak self]` in swift closure?

Your pattern has race condition. If self was deallocated at the exact same time as your completion handler closure was executing, it could crash. As a general rule, avoid using the ! forced unwrapping operator if you can.

  1. I’d lean towards the guard “early exit” pattern (reducing nested braces, making code easier to read). The standard Swift 4.2 solution is:

    someTask { [weak self] result in
    guard let self = self else { return }

    self.xxx = yyy
    self.doLongTermWork()
    self.finish()
    }
  2. Before Swift 4.2, which implemented SE-0079, we would have to do something like:

    someTask { [weak self] result in
    guard let strongSelf = self else { return }

    strongSelf.xxx = yyy
    strongSelf.doLongTermWork()
    strongSelf.finish()
    }

    You can see why we prefer the Swift 4.2 improvement, as this strongSelf syntax is inelegant.

  3. The other obvious alternative is just:

    someTask { [weak self] result in
    self?.xxx = yyy
    self?.doLongTermWork()
    self?.finish()
    }

    Sometimes you need the “weak self - strong self dance” (the first two alternatives), but it would not appear to be the case here. This is likely sufficient.

There are other scenarios/edge cases that one might contemplate, but these are the basic approaches.

Are `self?` and `guard let self` the same, in a closure?

These are practically identical. The main difference is that the first will check whether self is nil twice rather than once. Since the first example doesn't hold a strong reference across statements, it is technically possible for the first line to execute, then self to be released, and the second line not execute. In the second case, the guard let takes a strong reference until the end of the block, at which point self will be released.

The latter is generally preferable, though it's not a huge issue either way. It is easier to reason about the guard let code when self is nil. When you skip over a bunch of statements using self?., any lines that don't rely on self still execute, which might be surprising. It is also nice in cases where optional chaining isn't appropriate (for example when accessing properties of self). So it works the same way in more cases.

Shall we always use [unowned self] inside closure in Swift

No, there are definitely times where you would not want to use [unowned self]. Sometimes you want the closure to capture self in order to make sure that it is still around by the time the closure is called.

Example: Making an asynchronous network request

If you are making an asynchronous network request you do want the closure to retain self for when the request finishes. That object may have otherwise been deallocated but you still want to be able to handle the request finishing.

When to use unowned self or weak self

The only time where you really want to use [unowned self] or [weak self] is when you would create a strong reference cycle. A strong reference cycle is when there is a loop of ownership where objects end up owning each other (maybe through a third party) and therefore they will never be deallocated because they are both ensuring that each other stick around.

In the specific case of a closure, you just need to realize that any variable that is referenced inside of it, gets "owned" by the closure. As long as the closure is around, those objects are guaranteed to be around. The only way to stop that ownership, is to do the [unowned self] or [weak self]. So if a class owns a closure, and that closure captures a strong reference to that class, then you have a strong reference cycle between the closure and the class. This also includes if the class owns something that owns the closure.

Specifically in the example from the video

In the example on the slide, TempNotifier owns the closure through the onChange member variable. If they did not declare self as unowned, the closure would also own self creating a strong reference cycle.

Difference between unowned and weak

The difference between unowned and weak is that weak is declared as an Optional while unowned is not. By declaring it weak you get to handle the case that it might be nil inside the closure at some point. If you try to access an unowned variable that happens to be nil, it will crash the whole program. So only use unowned when you are positive that variable will always be around while the closure is around

Are there potential drawbacks to using a [weak self] within a closure after declaring a strong self?

Let's take this step by step (line by line)

// Weak 1
runClosure { [weak self] in

The first line creates a reference to the target object, a reference which coincidentally (or not) is named self.

    self?.isInner = false

The above line makes use of the weak reference, it has no effects on the target object lifecycle.

    guard let strongSelf = self else { return }

Now, this line indeed creates a strong reference to the target object, which will extend objects's lifetime by at least the lifetime of strongSelf. Now depending on the compiler aggressiveness, strongSelf might die after passing this line (the last reference in code), or when the outer closure finishes executing.

    // Can this [weak self] create problems?
// Weak 2
strongSelf.runClosure { [weak self] in

Now this has almost exactly the same effect as the capture from the outer closure. It creates a reference to a possibly already deallocated instance (depending if strongSelf is still alive at this point or not).

            self?.isInner = true

This is the regular optional usage, no effects on target lifetime.

Now, assuming that runClosure runs asynchronously, there are no issues with the target object living more that expected (assuming there aren't any more strong references out there).

To summarize, an object lifetime is determined by the number of strong references exist to that object. Once all strong references are destroyed, the object will be automatically deallocated. In your particular scenario, the inner closure weakly captures and already weak reference, and this doesn't impact the lifecycle of the target object. The only player is strongSelf, which gets destroyed no later than the outer closure destroyal.

Naming convention for weakly capturing self in a closure

As of Swift 4.2 you can use guard let self = self else { return } to unwrap weak self. There is no need to use strongSelf or `self` compiler bug for unwrapping.

You can read more about it in the Swift evolution proposal.

For swift references within view model, should self reference be weak or unknown?

You said:

I … was a little thrown off by the quote above. Am I missing something?

The difference between weak and unowned is merely that when the object is deallocated, it will go through an extra step and go back and nil the weak references, but not the for unowned references. The code with unowned references can therefore be slightly more efficient. But one must be scrupulous about when you use unowned, as the app will crash if you try to use that reference after that object has been deallocated.

What are best practices within the view model?

Personally, I would just use weak. The overhead is immaterial and the presence of asynchronous code significantly complicates the reasoning about unowned references and whether the object be deallocated by the time one reaches the unowned reference.

Lastly, within the closure I am setting self to optional, should I force unwrap? Is there any case in which a view model would be deallocated from memory?

You absolutely do not want to force unwrap whenever dealing with asynchronous code. What if the view and its associated view model were deallocated by the time the asynchronous completion handler code runs?! You should only force unwrap if you know that it can never be nil (which is not the case here).

Is it safe to force unwrap variables that have been optionally accessed in the same line of code?

Optional Chaining
from "The Swift Programming Language"
gives the following example:

 let john = Person()
// ...
let someAddress = Address()
// ...
john.residence?.address = someAddress

followed by (emphasis added):

In this example, the attempt to set the address property of john.residence will fail, because john.residence is currently nil.

The assignment is part of the optional chaining, which means none of the code on the right hand side of the = operator is evaluated.

Applied to your case: In

self?.variable = self!.otherVariable

the right-hand side is not evaluated if self is nil.
Therefore the answer to your question

If self indeed is nil, the second part will never happen?

is "yes". With regard to the second question

And it will never happen that self could be 'nilled' during this single line of code?

My original assumption was that once self has been determined to be != nil,
a strong reference to self! is held throughout the evaluation of the
statement, so that this can not happen. However (as @Hamish pointed out),
this is not guaranteed. Apple engineer Joe Groff writes at Confirming order of operations in
the Swift forum:

This isn't guaranteed. Releases may be optimized to happen earlier than this, to any point after the last formal use of the strong reference. Since the strong reference loaded in order to evaluate the left-hand side weakProperty?.variable is not used afterward, there is nothing keeping it alive, so it could be immediately released.

If there are any side effects in the getter for variable that cause the object referenced by weakProperty to be deallocated, nil-ing out the weak reference, then that would cause the force-unwrap on the right side to fail.
You should use if let to test the weak reference, and reference the strong reference bound by the if let



Related Topics



Leave a reply



Submit