Escaping and Non-Escaping Closures in Swift
In swift, closures can be defined as the self-contained block of code that can be passed in methods or used in our code. 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. Closures can capture and store references to any constants and variables from the context in which they're defined. This is known as closing over those constants and variables.
In this article, we will discuss escaping and non-escaping closures. We will also discuss why we should use escaping closures in our code.
When we pass a closure as an argument to the function, the function executes the closure and returns the compiler. Here, as soon as the closure gets executed and execution ends, the passed closure goes out of the scope, and therefore, it has no more existence in the memory. The non-escaping closures transitions into the following states in their lifecycle span.
Let's consider the following example in which the function accepts a non-escaping closure as the argument.
In the above example, we passed the closure in a function that prints the sum of the array being passed as other argument in the same function. Here, the function calculate_Sum performs the closure execution at the end of the body.
Here, we are not escaping the execution of the closure. Therefore, the closure has no existence in the memory once it gets executed.
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. We can use the escaping closures according to our requirements. There are the following ways to escape the closure.
One way to escape closure is to preserve the closure in the variable defined outside of the function and execute it later. This approach is used in the scenario where we need to use the function that starts an asynchronous operation and takes closure argument as the completion handler. The function returns after it starts then operation, but the closure argument is preserved to be called later on.
Consider the following example in which the calculate_Sum() accepts an escaping closure as the argument preserved in completionHandler, which is a similar closure type variable. Here, the completionHandler is called after the function gets executed.
When we execute closures asynchronously on DispatchQueue, the closure is preserved in the memory by the queue to use in the future. In this case, we cannot determine when the closure will get executed. Consider the following example.