As a follow up from this discussion earlier this year, here's a pitch for allowing implicit self for weak self captures. This is implemented in apple/swift#40702, and the full proposal document is here.
Introduction
As of SE-0269, implicit self is permitted in closures when self is written explicitly in the capture list. We should extend this support to weak self captures, and permit implicit self as long as self has been unwrapped.
class ViewController {
let button: Button
func setup() {
button.tapHandler = { [weak self] in
guard let self = self else { return }
dismiss()
}
}
func dismiss() { ... }
}
Motivation
Explicit self has historically been required in closures, in order to help prevent users from inadvertently creating retain cycles. SE-0269 relaxed these rules in cases where implicit self is unlikely to introduce a hidden retain cycle, such as when self is explicitly captured in the closure's capture list:
button.tapHandler = { [self] in
dismiss()
}
SE-0269 left the handling of weak self captures as a future direction, so explicit self is currently required in this case:
button.tapHandler = { [weak self] in
guard let self = self else { return }
self.dismiss()
}
Since self has already been captured explicitly, there is limited value in requiring authors to use explicit self. This is inconsistent, and adds unnecessary visual noise to the body of closures using weak self captures.
Proposed solution
We should permit implicit self for weak self captures, once self has been unwrapped.
This code would now be allowed to compile:
class ViewController {
let button: Button
func setup() {
button.tapHandler = { [weak self] in
guard let self = self else { return }
dismiss()
}
}
func dismiss() { ... }
}
Detailed design
Like with implicit self for strong and unowned captures, the compiler will synthesize an implicit self. for calls to properties / methods on self inside a closure that uses weak self.
If self has not been unwrapped yet, the following error will be emitted:
button.tapHandler = { [weak self] in
// error: explicit use of 'self' is required when 'self' is optional,
// to make control flow explicit
// fix-it: reference 'self?.' explicitly
dismiss()
}
Like in SE-0269, the innermost closure most capture self explicitly in order to use implicit self.
execute { [weak self] in
guard let self = self else { return }
execute {
// call to method 'operation' in closure requires explicit use of 'self' to make capture semantics explicit
dismiss()
}
}
Alternatives considered
It is technically possible to also support implicit self before self has been unwrapped, like:
button.tapHandler = { [weak self] in
dismiss() // as in `self?.dismiss()`
}
That would effectively add implicit control flow, however. dismiss() would only be executed when self is not nil, without any indication that it may not run. We could create a new way to spell this that still implies optional chaining, like ?.dismiss(), but that is not meaningfully better than the existing self?.dismiss() spelling.