Capture List in Swift

Before diving into Capture List, let's talk about closure cycles. Here is the definition from Apple docs

Closures are self-contained blocks of functionality that can be passed around and used in your code. Closures in Swift are similar to blocks in C and Objective-C and to lambdas in other programming languages.
Closures can capture and store references to any constants and variables from the context in which they are defined. This is known as closing over those constants and variables. Swift handles all of the memory management of capturing for you.

Here is an example of a closure

The closure increment the number’s value each time when I call increment()

Because Closures are reference types, so we could simulate a closure using a class since Classes are also reference types.

Whenever I change call that method, it increments that stored property.

Now here is an example that can cause a leak.

I change incrementNumber method to a closure and as a stored property, the output give exactly number I want. But this will cause memory leak, the closure refers back to the object itself, it refers to self in order to increment the number, and that will create a reference cycle:

  • We have an object and the object has a stored property that refers to a closure.
  • That closure refers back to self (means Increment instance)

let’s add deinitmethod and check if it has a retain cycle

The increment() instance should be release memory when the block (Curly braces) end.

Capture List will help us to fix it?

By adding [unowned self]

Then the deinit is printed out, that means everywhere that we refer to self in the closure block, it is going to be unowned, there will not be a strong reference back to self, so there is no leak.

However you should carefull when using [unowned self].

If I instantiate increment(), then I immediately call increment with a three, the program will crash, because when the stored property has returned, the object (increment instance) can be deallocated, nothing else is referring to it.

So how to fix it, let’s change [unowned self] to [weak self], that means that everywhere that self is accessed, we treat it as a weak property.

When the stored property has returned, if the object be deallocated, mean self is nil, then the number will not be incremented. This code will make it easy to handle if self is nil

And that’s it, I hope you enjoy this article, thanks for reading and I will see you next time.