Blog

Swift Tip: Capture Lists

Since Swift is a reference-counted language, we have to be careful not to create reference cycles — especially when we’re capturing self within a closure. Consider this code snippet, adapted from our new book, App Architecture:

final class PlayViewController: UIViewController {
    let viewModel = PlayViewModel()
    var observation: NSKeyValueObservation?

    func viewDidLoad() {
        super.viewDidLoad()
        // ...
        observation = viewModel.observe(\.navigationTitle, options: [.initial, .new]) { [unowned self] _, change in
            guard let v = change.newValue else { return }
            self.navigationItem.title = v
        }
    }
    // ...
}

Here we use the [unowned self] capture list to avoid a reference cycle. We could also have captured self weakly, but since we can guarantee that the lifetime of the observation is coupled to the lifetime of the view controller, we can get away with using unowned.

A few weeks ago, co-author Matt Gallagher pointed out an interesting alternative that we hadn’t considered before: instead of capturing an unowned reference to self, we can also use the capture list to capture a strong reference to the navigation item directly:

observation = viewModel.observe(\.navigationTitle, options: [.initial, .new]) { [navigationItem] _, change in
    guard let v = change.newValue else { return }
    navigationItem.title = v
}

This way the closure doesn’t capture self at all, but only the reference to the navigation item it actually needs. The benefit is that we can avoid the somewhat “dangerous” unowned reference to self while also not having to deal with a weak optional self within the closure.

Finally, we can even assign a different name to the captured reference if we want to avoid shadowing the name of the navigationItem property on the view controller:

observation = viewModel.observe(\.navigationTitle, options: [.initial, .new]) { [navItem = navigationItem] _, change in
    guard let v = change.newValue else { return }
    navItem.title = v
}

The lesson here is to consider carefully what you actually need to capture in your closure. Do you really need a reference to self? Or do you just need a reference to an object on self? If you can capture something more specific, you can often avoid dealing with weak or unowned references.

To learn more, our latest books deal with advanced concepts in Swift.


Stay up-to-date with our newsletter or follow us on Twitter.

Back to the Blog

recent posts