I've noticed that in Swift 2.2, closures marked as non-escaping with @noescape
do not require an explicit self
. In Swift 3, all closures are non-escaping by default and now requires them to be marked with @escaping
if you want them be able to escape.
Given that all of the closures in Swift 3 by default are non-escaping, why do they require an explicit self
?
final class SomeViewController: NSViewController {
var someClosure: () -> () = { _ in }
override func viewDidLoad() {
super.viewDidLoad()
someClosure = {
view.layer = CALayer() // ERROR: Implicit use of `self` in closure; use `self.` to make capture semantics explicit
}
}
}
Wrapping Up. In Swift, closures are non-escaping by default. This means that the closure can't outlive the function it was passed into as a parameter.
Escaping closures are different from non-escaping closures since we can preserve escaping closures to execute them later on. Meanwhile, the function body can execute and returns the compiler back. The scope of the escaping closure exists and has existence in memory as well until it gets executed.
A closure is said to escape a function when the closure is passed as an argument to the function, but is called after the function returns. When you declare a function that takes a closure as one of its parameters, you can write @escaping before the parameter's type to indicate that the closure is allowed to escape.
Functions and closures are first-class objects in Swift: you can store them, pass them as arguments to functions, and treat them as you would any other value or object. Passing closures as completion handlers is a common pattern in many APIs. Standard Swift library uses closures mostly for event handling and callbacks.
In swift 5, closure parameters are non-escaping by default. Closures can also be executed within the function body; if we require escaping closure, we can mark it as @escaping. Closures can capture and store references to any constants and variables from the context in which they're defined.
However, we can define two types of closures, i.e., escaping and non-escaping closures. In swift 5, closure parameters are non-escaping by default. Closures can also be executed within the function body; if we require escaping closure, we can mark it as @escaping.
Closure expressions are unnamed closures written in a lightweight syntax that can capture values from their surrounding context. Swift’s closure expressions have a clean, clear style, with optimizations that encourage brief, clutter-free syntax in common scenarios. These optimizations include: Inferring parameter and return value types from context
“A closure is said to escape a function when the closure is passed as an argument to the function, but is called after the function returns.” In order to be an escaping closure, the function to which the closure is passed must return before the closure is executed. Ok, sounds good. When could this happen?
In Swift 3, all closures are non-escaping by default
No, in Swift 3, only closure function arguments (i.e function inputs that are functions themselves) are non-escaping by default (as per SE-0103). For example:
class A {
let n = 5
var bar : () -> Void = {}
func foo(_ closure: () -> Void) {
bar = closure // As closure is non-escaping, it is illegal to store it.
}
func baz() {
foo {
// no explict 'self.' required in order to capture n,
// as foo's closure argument is non-escaping,
// therefore n is guaranteed to only be captured for the lifetime of foo(_:)
print(n)
}
}
}
As closure
in the above example is non-escaping, it is prohibited from being stored or captured, thus limiting its lifetime to the lifetime of the function foo(_:)
. This therefore means that any values it captures are guaranteed to not remain captured after the function exits – meaning that you don’t need to worry about problems that can occur with capturing, such as retain cycles.
However, a closure stored property (such as bar
in the above example) is by definition escaping (it would be nonsensical to mark it with @noescape
) as its lifetime not limited to a given function – it (and therefore all its captured variables) will remain in memory as long as the given instance remains in memory. This can therefore easily lead to problems such as retain cycles, which is why you need to use an explicit self.
in order to make the capturing semantics explicit.
In fact, case in point, your example code will create a retain cycle upon viewDidLoad()
being called, as someClosure
strongly captures self
, and self
strongly references someClosure
, as it's a stored property.
It's worth noting that as an extension of the "stored function properties are always escaping" rule, functions stored in aggregates (i.e structures and enumerations with associated values) are also always escaping, as there's no restrictions on what you do with such aggregates. As pointed out by pandaren codemaster, this currently includes Optional
– meaning that Optional<() -> Void>
(aka. (() -> Void)?
) is always escaping. The compiler might eventually make this a special case for function parameters though, given that optional is already built on a lot of compiler magic.
Of course, one place where you would expect to be able to use the @noescape
attribute is on a closure that’s a local variable in a function. Such a closure would have a predictable lifetime, as long as it isn’t stored outside of the function, or captured. For example:
class A {
let n = 5
func foo() {
let f : @noescape () -> Void = {
print(n)
}
f()
}
}
Unfortunately, as @noescape
is being removed in Swift 3, this won't be possible (What's interesting is that in Xcode 8 GM, it is possible, but yields a deprecation warning). As Jon Shier says, we’ll have to wait for it to be re-added to the language, which may or may not happen.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With