Where does the weak self go?
First of all, note that you generally don't need to worry about retain cycles with DispatchQueue.main.asyncAfter
, as the closure will be executed at some point. Therefore whether or not you weakly capture self
, you won't be creating a permanent retain cycle (assuming that tickle.fresh
also doesn't).
Whether or not you put a [weak self]
capture list on the outer asyncAfter
closure depends entirely on whether you want self
to be retained until the closure is called (after the time you set). If you don't need self
to remain alive until the closure is called, put [weak self]
in, if you do, then don't put it in.
Whether or not you put a [weak self]
on the inner closure (the one passed to tickle.fresh
) depends on whether you've already weakly captured self
in the outer closure. If you haven't, then you can put [weak self]
in order to prevent the inner closure from retaining it. If however, the outer closure has already weakly captured self
, then the inner closure will already have a weak reference to self
, thus adding [weak self]
to the inner closure will have no effect.
So, to summarise:
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
tickle.fresh { msg in
self.paint()
}
}
self
will be retained by both the outer and inner closure.
DispatchQueue.main.asyncAfter(deadline: .now() + 2) { [weak self] in
tickle.fresh { msg in
self?.paint()
}
}
self
will not be retained by either closure.
DispatchQueue.main.asyncAfter(deadline: .now() + 2) { [weak self] in
tickle.fresh { [weak self] msg in
self?.paint()
}
}
Same as the above, the additional [weak self]
for the inner closure has no effect, as self
is already weakly captured by the outer closure.
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
tickle.fresh { [weak self] msg in
self?.paint()
}
}
self
will be retained by the outer closure, but not the inner closure.
Of course, it might be that you don't want self
to be retained by the outer closure, but you do want it to be retained by the inner closure. In such cases, you can declare a local variable in the outer closure in order to hold a strong reference to self
, when you can then capture in the inner closure:
DispatchQueue.main.asyncAfter(deadline: .now() + 2) { [weak self] in
guard let strongSelf = self else { return }
tickle.fresh { msg in
strongSelf.paint()
}
}
Now, self
won't be kept alive by the outer closure, but once it's called, if self
still exists, it will be kept alive by the inner closure until that closure has been deallocated.
In response to:
Is a strong reference to a weak reference, a weak or strong reference?
Weak references are implemented as optionals, which are value types. Therefore you cannot directly have a strong reference to one – instead you first have to unwrap it, and then take a strong reference to the underlying instance. In this case you're simply dealing with a strong reference (exactly like my example above with strongSelf
).
However, if a weak reference is boxed (this happens with closure capture – the value type will be put into a heap-allocated box) – then you can indeed have a strong reference to that box. The effect of this is equivalent to a weak reference to the original instance, you just have an invisible bit of extra indirection.
In fact, this is exactly what happens in the example where the outer closure weakly captures self
and the inner closure 'strongly captures' that weak reference. The effect is that neither closure retains self
.
What is difference between [self] in & [weak self] in in Swift?
Writing [self]
is same as leaving the closure as it is meaning it could cause retain cycle unlike [weak self]
, BTW no need for any of them with DispatchQueue.main.async {
as GCD queues don't hold a strong reference to self
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.
Swift [weak self] for Dispatching on main in a nested closure
You're way overthinking this. The need for weak self has nothing to do with what thread you're going to be on, and it has nothing to do with dispatch queues. If you're going to be worrying about weak self every time you see a pair of curly braces, you'll be a nervous wreck. /p>
Weak self has to do with whether there will be a retain cycle. Retain cycles are caused through long term storage of a closure by an object it captures.
Nothing like that is going on here. When you are just saying "perform this action now", regardless of the thread, no retain cycle arises. So no precautions are necessary.
Weak self in closures and consequences example
The question should not be "can I use weak reference," but rather "should I use weak reference." You use weak references to avoid strong reference cycles or to keep a closure from hanging on to something after it may have been disposed. But don't just add weak references because you can.
In case 1, you probably do want to use
[weak self]
. Why? Because if the view controller was dismissed while the authorization was underway, do you really want to keep a reference to a view controller that was dismissed? Probably not in this case.In case 2, you theoretically could use
[weak self]
in theanimation
block, but why would you? There's no reason to. The weak reference is something you do with completion handlers and/or closure variables, but for an animation block it offers no utility, so I wouldn't do it there. To useweak
here suggests a misunderstanding of the memory semantics involved.In case 3, you have two separate issues.
In the
didSelectItemAtIndexHandler
, that probably should use[unowned self]
because the object's own closure is referring to itself.It may be a moot issue, as I don't see you actually using that
BTNavigationDropdownMenu
(perhaps that initializer is adding itself to the navigation controller, but that's not a well designed initializer if so, IMHO).But as a general concept, when an object has a handler closure that can only be called when the object is still around, but shouldn't, itself, cause the object to be retained, you'd use
[unowned self]
.In the broader
checkUserLoggedIn
closure, the question is whether that's a completion handler. If so, you probably should use[weak self]
, because this could be initiated and be running by the timeself
is dismissed, and you don't want to havecheckUserLoggedIn
keep a reference to a view controller that was dismissed. But you wouldn't want to use[unowned self]
because that would leave you with dangling pointers if it had been released by the time the closure runs.As an aside, you contemplate:
weak var weakSelf = self
That is a bit unswifty. You would use the
[weak self]
pattern at the start of thecheckUserLoggedIn
closure. If you have an example where you're tempted to useweak var weakSelf = ...
, you should edit your question, including an example of where you want to use that pattern. But this is not one of those cases.
async/await, Task and [weak self]
Bottom line, there is often little point in using [weak self]
capture lists with Task
objects. Use cancelation patterns instead.
A few detailed considerations:
Weak capture lists are not required.
You said:
in traditional concurrency in Swift, if you are performing (for example) a network request inside a class, and in the completion of that request you reference a function that belongs to that class, you must pass
[weak self]
…This is not true. Yes, it may be prudent or advisable to use the
[weak self]
capture list, but it is not required. The only time you “must” use aweak
reference toself
is when there is a persistent strong reference cycle.For well-written asynchronous patterns (where the called routine releases the closure as soon as it is done with it), there is no persistent strong reference cycle risk. The
[weak self]
is not required.Nonetheless, weak capture lists are useful.
Using
[weak self]
in these traditional escaping closure patterns still has utility. Specifically, in the absence of theweak
reference toself
, the closure will keep a strong reference toself
until the asynchronous process finishes.A common example is when you initiate a network request to show some information in a scene. If you dismiss the scene while some asynchronous network request is in progress, there is no point in keeping the view controller in memory, waiting for a network request that merely updates the associated views that are long gone.
Needless to say, the
weak
reference toself
is really only part of the solution. If there’s no point in retainingself
to wait for the result of the asynchronous call, there is often no point in having the asynchronous call continue, either. E.g., we might marry aweak
reference toself
with adeinit
that cancels the pending asynchronous process.Weak capture lists are less useful in Swift concurrency.
Consider this permutation of your
function2
:func function2() {
Task { [weak self] in
let result = await apiClient.performNetworkRequestAsync()
self?.printSomething()
}
}This looks like it should not keep a strong reference to
self
whileperformNetworkRequestAsync
is in progress. But the reference to a property,apiClient
, will introduce a strong reference, without any warning or error message. E.g., below, I letAsyncClass
fall out of scope at the red signpost, but despite the[weak self]
capture list, it was not released until the asynchronous process finished:The
[weak self]
capture list accomplishes very little in this case. Remember that in Swift concurrency there is a lot going on behind the scenes (e.g., code after the “suspension point” is a “continuation”, etc.). It is not the same as a simple GCD dispatch. See Swift concurrency: Behind the scenes.If, however, you make all property references
weak
, too, then it will work as expected:func function2() {
Task { [weak self] in
let result = await self?.apiClient.performNetworkRequestAsync()
self?.printSomething()
}
}Hopefully, future compiler versions will warn us of this hidden strong reference to
self
.Make tasks cancelable.
Rather than worrying about whether you should use
weak
reference toself
, one could consider simply supporting cancelation:var task: Task<Void, Never>?
func function2() {
task = Task {
let result = await apiClient.performNetworkRequestAsync()
printSomething()
task = nil
}
}And then,
@IBAction func didTapDismiss(_ sender: Any) {
task?.cancel()
dismiss(animated: true)
}Now, obviously, that assumes that your task supports cancelation. Most of the Apple async API does. (But if you have written your own
withUnsafeContinuation
-style implementation, then you will want to periodically checkTask.isCancelled
or wrap your call in awithTaskCancellationHandler
or other similar mechanism to add cancelation support. But this is beyond the scope of this question.)
Avoiding using self in the DispatchQueue
Don't worry! DispatchQueue
closures don't cause retain cycles.
Related Topics
Rounding a Double Value to X Number of Decimal Places in Swift
#Ifdef Replacement in the Swift Language
Waiting Until the Task Finishes
Xcode 11 & Ios13, Using Uikit Can't Change Background Colour of Uiviewcontroller
How to Use Attributed String in Swiftui
Check If 'Any' Value Is Object
Swiftui Iterating Through Dictionary With Foreach
How to Get the Name of Enumeration Value in Swift
Ios 11 Custom Navbar Goes Under Status Bar
How to Load Url in Uiwebview in Swift
How to Pass Multiple Enum Values as a Function Parameter
Changing Text Color of Datepicker
Swiftui Picker Separate Texts For Selected Item and Selection View