Open In App

Escaping and Non-Escaping Closures in Swift

Improve
Improve
Like Article
Like
Save
Share
Report

In Swift, a closure is a self-contained block of code that can be passed to and called from a function. Closures can be passed as arguments to functions and can be stored as variables or constants. Closures can be either escaping or non-escaping. An escaping closure is a closure that is called after the function it was passed to returns. In other words, an escaping closure outlives the function it was passed to. A non-escaping closure, on the other hand, is a closure that is called within the function it was passed to before the function returns.

Escaping Closures

A closure that is called after the function it was supplied to returns is referred to as an escaping closure. It outlives the function it was supplied to, in other words. The completion handler is an illustration of an escaping closure. A completion handler is often run in the future, depending on how it is implemented. When a task takes a long time to finish, the closure escapes and outlives the purpose for which it was designed. An escaping closure can be thought of as code that outlives the function it was provided into. Imagine that function as a prison, and the closure as a bandit breaking out of the prison.

The @escaping closure lifecycle is as follows:

  • When calling the function, pass the closure as a parameter.
  • Put a little extra effort into function.
  • Asynchronously or stored, the function executes the closure.
  • The compiler is returned by the function.

Example:

In this code, we are using an escaping closure to asynchronously load data from a URL. The loadData function takes an escaping closure as an argument and executes it asynchronously on the main queue after the data has been loaded. The closure is marked as escaping because it is stored and called after the loadData function has returned.

The closure takes a single argument, data, which is an optional Data object. If the data is nil, it means that there was an error loading the data and the guard statement will handle the error. Otherwise, the closure will continue to execute and the data can be used.

Swift




// Swift function to call the escaping closure
import Swift
import Foundation
import FoundationNetworking
  
let url = URL(string: "https://www.geeksforgeeks.org/")!
let data = try? Data(contentsOf: url)
  
// Function to call the escaping closure
func loadData(completion: @escaping (_ data: Data?) -> Void
{
    DispatchQueue.global().async 
    {
        let data = try? Data(contentsOf: url)
        DispatchQueue.main.async 
        {
            completion(data)
        }
    }
}
  
// use data
loadData { data in
        guard data != nil else {
      
    // Handle error
    return
    }
}
     
// Print result
print("Data loaded")


After the call to loadData, you have a print statement that will print the message “Data loaded” to the console once the data has been loaded and the closure has been executed.

Output: 

Data loaded

Non-Escaping Closures

A closure that is called inside the function it was supplied into, that is, before it returns, is referred to as a non-escaping closure. This closure never exceeds the function it was supplied into boundaries. The function, when given a closure as a parameter, executes the closure and returns the compiler. The passed closure in this case no longer exists in memory once the closure has been executed and execution has concluded since it has left the scope. During the course of their lifetime, non-escaping closures change into different states.

The @nonescaping closure lifecycle is as follows:

  • When calling the function, pass the closure as a parameter.
  • Do some further work with function.
  • The closure is run by the function.
  • The function gives back the compiler.

Example:

In the following example, the doWork function takes a non-escaping closure as an argument. The closure is executed synchronously within the current function and is not allowed to be stored or called after the current function returns. The closure does not take any arguments and does not return a value. 

Swift




// Swift function to call the non-escaping closure
import Swift
import Foundation
import FoundationNetworking
  
// Function to call the non-escaping closure
func doWork(completion: () -> Void
{
  
    // Perform some work
    completion()
}
  
// Print result
doWork 
{
    print("Work is complete")
}


It simply prints the message “Work is complete” to the console.

Result:

Work is complete

It’s important to mark closures as escaping when necessary because it allows the compiler to manage the memory of the closure more effectively. If you pass an escaping closure to a function and forget to mark it as escaping, you’ll get a compile-time error.

Autoclosures

An autoclosure is a closure that is automatically created to wrap a piece of code. It is a way to delay the execution of closure until it is actually needed. Autoclosures are often used as arguments to functions where the closure is optional or where the closure’s execution may not always be necessary.

Example:

Swift




// Swift program using autoclosure
import Swift
import Foundation
  
// Function using autoclosure
func logIfTrue(_ condition: @autoclosure () -> Bool
{
    if condition() 
    {
        print("True")
    
    else 
    {
        print("False")
    }
}
  
logIfTrue(2 > 1)


In this example, the logIfTrue function takes an autoclosure as an argument. The autoclosure is created by wrapping the 2 > 1 expression in parentheses. The logIfTrue function then calls the autoclosure to evaluate the condition.

Output:

True


Last Updated : 17 Mar, 2023
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads