Leaking closures in Swift

Leaking closures in Swift

Swift Leaking

Since a few months I have the common practice to directly map closures to a function. It cleans up the code a bit, and moves the actual functionality to somewhere else in your code.

Instead of doing:

viewModel.completion {
    self.triggerAction()
}

You could do this:

viewModel.completion = triggerAction

Mapping the triggerAction to the completion closure can only be done when the function's signature is the same as the closure one.

Another nice example is dispatching a function to the main thread:

DispatchQueue.main.async(execute: collectionView.reloadData)

This triggers the reloadData() function on the collectionView without you having to use the verbose way.

You could also animate the layout invalidation this way:

UIView.animate(withDuration: 0.35, 
               animations: view.layoutIfNeeded)

Weak

Now it could occur that you perform an asynchronous task and at the end of such a task a callback is triggered. But what if the object that implements this callback is deallocated before the closure is triggered?

Well that is when [weak self] comes into play.

viewModel.completion = { [weak self] in
    self?.triggerAction()
}

When the task finishes it will trigger the completion handler. If the object becomes deallocated, the reference to self will be nil and triggerAction will not be called.

More information on the difference between weak and unowned can be found in this great blog post by Hector Matos.

But what happens when we want to use our cleaner solution?

viewModel.completion = triggerAction

Well, this can lead to a memory leak. The closure is responsible for the implementing object not being deallocated. And that is something you do not want to happen. It has the same effect as not using [weak self].

Life finds a way

But what if you want to continue to use our clean solution by passing the function to the closure? I prefer to have it done like this to make my code a bit less verbose.

Luckily there is a solution for this.

You can create a lazy function with the same signature as the triggerAction function. So in this case it would be:

lazy var triggerAction: () -> Void = {
    // Perform the same code as in the original function.
}

This solution has the same clean code as before, but without the memory leak. You can even manually call this lazy block property as you would to as if it were a function.

I hope you enjoyed this blog post and that it helps you getting your code a bit cleaner. Let us know what you think about this approach.