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.