Bad practice: capturing self in a nested closure
You’re more of a video kind of person? I’ve got you covered! Here’s a video with the same content than this article 🍿
Can you guess what’s the problem with this code?
Of course, having two completion handlers nested into each other isn’t a great architecture, but it’s actually far from the biggest issue!
The big issue is that this code can lead to a memory leak 😱
This seems bit crazy, because on the surface this code seems perfectly safe: we’re capturing a weak
reference to self
just like we’re supposed to.
So where does the issue come from?
Let’s take a closer look at the place where we capture the weak
reference.
We’re capturing the weak
reference inside the nested closure.
And the thing is, when a closure captures a weak
reference, it captures it from its parent scope.
And here that parent scope is the first closure.
This means that the first closure has to implicitly capture a strong reference to self
, so that the second closure is then able to capture a weak
reference to it.
And so using [weak self]
inside the second closure actually has the same effect than if we had explicitly captured a strong reference to self
in the first closure…
Fortunately, this very tricky issue is quite easy to fix!
You just need to move the capture of the weak
reference from the nested closure to the parent closure.
Now both closures have access to the weak
reference and there’s no more risk of a retain cycle or memory leak!
That’s all for this article: I hope it will help you avoid sneaky memory leaks in the future!
Here’s the code if you want to experiment with it:
firstCall { [weak self] in
secondCall {
self?.doSomething()
}
}