In Swift, there are two ways to capture self
as a strong reference within an escaping closure. The first is to explicitly use the self
keyword whenever we’re calling a method or accessing a property on the current object within such a closure.
For example, the following VideoViewController
performs such a strong capture in order to be able to call two of its own methods whenever it finished preparing its Video
model:
class VideoViewController: UIViewController {
private var video: Video
...
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
prepareVideo {
self.movePlayhead(to: self.video.lastPlayheadPosition)
self.startPlaybackIfNeeded()
}
}
}
The reason that it’s safe to capture self
strongly within the above example is because the closure that we pass to prepareVideo
isn’t stored in a way that would potentially cause a retain cycle. For more on that topic, check out “Is using [weak self] always required when working with closures?”.
The above is certainly the most well-known way to access properties and methods on self
within an escaping closure. But there’s also another, lesser-known technique that lets us reference self
just once — and that’s to use a capture list to set up our reference. While capture lists are most commonly used when we want to create a weak or unowned reference to self
, or when we want to capture a specific set of properties, they can also be used in situations like the above in order to avoid having to retype that same self
prefix multiple times — like this:
class VideoViewController: UIViewController {
private var video: Video
...
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
prepareVideo { [self] in
movePlayhead(to: video.lastPlayheadPosition)
startPlaybackIfNeeded()
}
}
}
Note how we can now call methods and access properties on self
just like if we were writing our code in a context other than an escaping closure, which is quite neat!
Of course, we also always have the option to move the logic that we want to perform to a dedicated method instead, which we could then simply call from within our closure:
class VideoViewController: UIViewController {
private var video: Video
...
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
prepareVideo {
self.videoWasPrepared()
}
}
private func videoWasPrepared() {
movePlayhead(to: video.lastPlayheadPosition)
startPlaybackIfNeeded()
}
}
It’s important to re-iterate that the above techniques are specifically for creating strong references to objects. When we don’t want to retain the current object, we’d be much better off using either a weak or unowned self
capture, or to avoid capturing self
entirely. For more on that topic, check out “Swift’s closure capturing mechanics”. Also, when working with value types, the compiler will implicitly capture self
for us, which you can read more about here.
Thanks for reading, and Happy New Year! 🎉