https://swiftunboxed.com/lang/closures-escaping-noescape-swift3/
Closures
Closures in Swift are similar to that of self-contained functions organized as blocks and called anywhere like C and Objective C languages.
In Swift, functions are named closures.
Constants and variable references defined inside the functions are captured and stored in closures. Functions are considered as special cases of closures.
Closures are defined like so:
1 2 3 |
{(parameters) -> return type in statements } |
In the below case it is:
1 2 3 4 5 6 7 8 9 |
// {() -> () in // statements // } let studname = { // no paramter or return values, hence empty println("Welcome to Swift Closures") } studname() |
output:
Welcome to Swift Closures
The following closure accepts two parameters and returns a Bool value −
1 2 3 4 5 6 7 8 9 |
// {(Int, Int) -> Int in // Statement n // } let divide = {(val1: Int, val2: Int) -> Int in return val1 / val2 } let result = divide(200, 20) println (result) |
if function expressions are declared and used like so:
1 2 3 4 5 |
func ascend(s1: String, s2: String) -> Bool { return s1 > s2 } let stringcmp = ascend("swift", "great") println (stringcmp) |
then closure expressions are used like this:
1 2 3 4 5 |
let sum = {(no1: Int, no2: Int) -> Int in return no1 + no2 } let digits = sum(10, 20) println(digits) |
Function usage
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
func jediGreet(name: String, ability: String) -> (farewell: String, mayTheForceBeWithYou: String) { return ("Good bye, \(name).", " May the \(ability) be with you.") } let retValue = jediGreet("old friend", "Force") println(retValue) println(retValue.farewell) println(retValue.mayTheForceBeWithYou) // returns function type where (function paramters (String, Int), function returns String) func jediTrainer () -> ((String, Int) -> String) { print("\n-->jediTrainer"); func train(name: String, times: Int) -> (String) { print("\n-->train"); return "\n \(name) has been trained in the Force \(times) times" } print("\n returning train function") return train } let train = jediTrainer() print("\n--------------------\n") print(train("Obi Wan", 3)) |
Thus, 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 in closures as completion handlers is a common pattern in many APIs we all know and love.
When you pass a closure into a function in Swift 3, there’s a new wrinkle: the compiler assumes closure parameters are non-escaping by default. What does this mean? What’s the difference between an escaping and a non-escaping closure?
Non-Escaping Closures
The lifecycle of a non-escaping closure is simple:
- 1.Pass a closure into a function
- 2a. The function runs the closure (or not)
- 2b. The function returns
- 3. When the function returns, the passed-in closure goes out of scope, thus has not escaped the function. Nothing else can reference it
Notice the closure has not escaped the body of the function. When the function ends, the passed-in closure goes out of scope and there were no additional references made to the closure.
If you remember your memory management, you might say the retains and releases are balanced; the closure object’s retain count after the function returns is the same as it was before the function was called.
Escaping Closures
Inside the function, you can still run the closure (or not); the extra bit is the closure is stored some place that will outlive the function. There are several ways to have a closure escape its containing function:
- Asynchronous execution: If you execute the closure asynchronously on a dispatch queue, the queue will hold onto the closure for you. You have no idea when the closure will be executed and there’s no guarantee it will complete before the function returns.
- Storage: Storing the closure to a global variable, property, or any other bit of storage that lives on past the function call means the closure has also escaped.
References
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
class ClassA { // takes a closure (non-escaping by default) func someMethod(closure: () -> Void) { // secret stuff } } class ClassB { let classA = ClassA() var someProperty = "Hello" func testClosure() { classA.someMethod { // self is captured! someProperty = "Inside the closure!" } } } |
When you call someMethod with the closure, note that someProperty is a property on ClassB. What happened to the requirement about always using self in closures? That requirement only applies to escaping closures and since non-escaping is the new default, you’ll have no problems running this code in Swift 3.
The closure is still capturing self but since the closure won’t live past calling someMethod, the compiler knows there’s no retain cycle risk: there’s no chance that closure parameter will make it out.
On the other hand, what if the method declaration looked like this:
1 2 3 |
func someMethod(closure: @escaping () -> Void) { // secret stuff } |
Now it’s a different story. Who knows what the method will do with the closure: store it, queue it somewhere? When you call this method and provide a closure that references a property, you must explicitly write self inside the closure body to remind yourself about the capture.
Capturing as weak
Note that closure parameters are non-escaping by default. However, closures still strong outside properties. Hence, you’ll need to do a weak self:
1 2 3 4 5 6 7 8 9 10 11 |
class ClassB { let classA = ClassA() var someProperty = "Hello" func testClosure() { classA.someMethod { [weak self] in // self is captured! self?.someProperty = "Inside the closure!" } } } |
The Least You Need to Know, and other examples
https://stackoverflow.com/questions/39433221/why-do-closures-require-an-explicit-self-when-theyre-all-non-escaping-by-defa
In Swift 3, closure parameters are non-escaping by default; you can use the new @escaping attribute if this isn’t what you want. Non-escaping closures passed in as arguments are guaranteed to not stick around once the function returns.
in Swift 3, only closure function arguments are non-escaping by default (as per SE-0103). For example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
class A { let n = 5 var bar : () -> Void = {} // stored property func foo(_ closure: () -> Void) { bar = closure // "closure" is a parameter, hence it is non-escaping, it is illegal to store it. } func baz() { foo { // no explicit '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 closure parameters 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.
Let’s say we have this code:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
final class SomeViewController: NSViewController { var someClosure: () -> () = { _ in } // self strongs someClosure override func viewDidLoad() { super.viewDidLoad() someClosure = { // the closure here captures self strongly. view.layer = CALayer() // ERROR: Implicit use of `self` in closure; use `self.` to make capture semantics explicit } } } |
In this example code, it 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.