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()
    }
}
Previous
Previous

Did you know you can use Copilot with an Xcode project? 🤖

Next
Next

Here are 3 cool new features of Swift 5.9 🤩