Swift blocks are an increasingly common way of providing callback type behavior to asynchronous functions.

Let’s first review what “capturing” means.

Swift blocks look like this:

doSomething(then: {
  // do something else
})

Due to the mechanics of memory management, if we need access to local variable within that block, it must be captured.

var dog = Dog()
doSomething(then: {
  dog.bark() // dog must be captured so it will live long enough
})

The Swift compiler automatically manages this for you. dog will be captured (thus increasing its retain count) and you can ensure that even though the dog instance falls out of the current scope, the block is still retaining it.

Sometimes you don’t want this behavior. The most common reason why is the case where you need to call a method on self inside the block. The block will retain self and you’ve just created a bi-directional dependency graph, otherwise known as a retain cycle.

retain-cycle

This is bad because it means self can’t ever be released as long as the block lives. Often times the blocks are stored as properties on the instance, or as observers for KVO or notifications, and their lifetime is bound to self.

If you’ve done this, congratulations… you have a memory leak.

Avoiding Retain Cycles

To avoid this, you can provide a capture binding declaration to the block definition to influence when a variable should be strongly held or not:

let dog = Dog()
doSomething(then: { [weak dog] in
    // dog is now Optional<Dog>
    dog?.bark()
)

(So unless we strongly reference dog in a property or through some other means, it will fall out of scope, and the dog reference inside the block will be nil.)

This works for self as well:

doSomething(then: { [weak self] in
    self?.doSomethingElse()
)

By declaring a capture as weak, you’re telling the block to “nil-out” this reference if the original is released.

The Strong-weak dance

If you need to guarantee that the items in the block execute you can create a new strong reference inside the block. For instance the variable became nil while the block was executing, preventing some of the behavior from executing.

doSomething(then: { [weak self] in
    guard let strongSelf = self { else return }
    strongSelf.doSomethingElse()
)

In the above example, we ensure that our strongSelf reference exists for the scope of the block.

I don’t care for this naming style, but it conveys exactly what’s going on here. strongSelf inside the block is a strongly reference variable that will exit scope at the end of the block, thereby releasing it’s reference to self.

There’s a more concise version of this that I prefer, as of Swift 5.6:

doSomething(then: { [weak self] in
    guard let self { else return }
    self.doSomethingElse()
)

This syntax works with any optional variable if the unwrapped version has the same name. This avoids the similar guard let self = self which is the same thing, but is more noise.

Nested Captures

What if you nest blocks?

doSomething(then: { [weak self] in
    guard let self { else return }
    self.doSomethingElse(then: {
        self.finish()
    })
)

Here the inner self refers to our unwrapped version. So what happens is the nested block captures self from the guard clause in the first closure. This means you don’t have to do the weak-strong dance within the second closure.

The newer syntax makes this kind of hard to see, but this is equivalent to:

doSomething(then: { [weak self] in
    guard let strongSelf = self { else return }
    strongSelf.doSomethingElse(then: {
        strongSelf.finish()
    })
)

Updated this post to reflect changes in Swift 5.6