Escape closure concept: A function that takes a closure as an argument and may not be called until the function returns, i.e., the closure has escaped from the function’s scope. When you declare a function that accepts closures as formal arguments, you can make it clear that closures are allowed to escape by writing @escaping before the formal argument.

Example: closure that is invoked when the network request ends. The closure is executed some time after the request is made, not necessarily within the scope of the function.

import UIKit class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() getData { (data) in Print (" Closure result returned --\(data)--\(thread.current)")}} func getData(Closure :@escaping (Any) -> Void) {closure:@escaping (Any) -> Void) { Print (start - \ "function (Thread. Current)") DispatchQueue. Global (). The async {DispatchQueue. Main. AsyncAfter (deadline: DispatchTime.now()+2, execute: {print (" performed closure - \ "(Thread. Current)) closure (" 345")})} print (" function performs end - \ [Thread. Current) ")}}Copy the code

The final execution result of the above code is:It can be seen from the results that the life of the escape closure is longer than that of the function.

The life cycle of an escape closure:

1. Closures are passed to functions as arguments;

2. Exit function;

The closure is called, and the closure life cycle ends

That is, the life of an escape closure is longer than that of a function. When the function exits, the reference to the escape closure is still held by other objects and will not be released at the end of the function.

Non-escaping closure concept: A function that takes a closure as an argument and the closure is called before the function terminates.

import UIKit class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() handleData { (data) in Print (" Closure return --\(data)--\(thread.current)")}} func handleData(closure (Any) -> Void) {closure (Any) -> Void) { Print (" function executed --\(thread.current)") print(" closure executed --\(thread.current)") closure("4456") Print (" end of function --\(thread.current)")}}Copy the code

The final execution result of the above code is:

As you can see from the results, the non-escape closure is confined to the function.

The life cycle of a non-escape closure:

1. Closures are passed to functions as arguments;

2. Run the modified closure in the function;

Exit the function.

In order to manage memory, a closure will forcibly reference all objects it captures. For example, if you access the properties and functions of the current controller in the closure, the compiler will ask you to display a reference to self in the closure. This way the closure will hold the current object, leading to circular references.

A non-escape closure does not generate a circular reference, it is released within the scope of the function, and the compiler is guaranteed that the closure will release any objects it captures at the end of the function; Another benefit of using non-escape closures is that the compiler can apply more powerful performance optimizations. For example, when the life cycle of a closure is known, some retain and release calls can be eliminated. In addition, memory for the context of a non-escape closure can be stored on the stack rather than on the heap.