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.
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()
}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.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 byweakProperty
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 theif let
Related Topics
Insertion-Order Dictionary (Like Java's Linkedhashmap) in Swift
Swift Realm, Load the Pre-Populated Database the Right Way
Cannot Use Mutating Member on Immutable Value of Type
Swift Protocol Generic as Function Return Type
Adding a View to the Window Hierarchy
!? Strange Double Unwrapped Optional Syntax in For_In []
How to Detect Switch Between MACos Default & Dark Mode Using Swift 3
Animate the Fractioncomplete of Uiviewpropertyanimator for Blurring the Background
Swift Convert Currency String to Double
Swift 3:Issue with Avvideocompositioncoreanimationtool to Add Watermark on Video
Binary Operator '+' Cannot Be Applied to Two 'T' Operands
Uitableview - Multiple Selection and Single Selection
Swift "Where" Array Extensions
Firebase Firestore Not Updating Email Verification Status
Avspeechutterance - Swift - Initializing with a Phrase
Display All Available Wifi Connections with Swift in Os X
How to Update Swift from 3.1 to 3.2, But Not to 4.0
Swiftui @Fetchrequest Core Data Changes to Relationships Don't Refresh