Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Escaping Closures in Swift

Tags:

closures

swift

I'm new to Swift and I was reading the manual when I came across escaping closures. I didn't get the manual's description at all. Could someone please explain to me what escaping closures are in Swift in simple terms.

like image 867
Nikhil Sridhar Avatar asked Sep 15 '16 06:09

Nikhil Sridhar


People also ask

What is difference between escaping closure and non-escaping closure in Swift?

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.

What are non-escaping closures?

An escaping closure is a closure that can outlive (escape) the scope where it was being used, that is, it can be called even after the function body is executed. On the other hand, a non-escaping closure goes out of the scope and stops existing in memory as soon as the function body gets executed.

Where closures are used in Swift?

Closures in Swift are similar to blocks in C and Objective-C and to lambdas in other programming languages. 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.

What are closures in Swift with example?

Closures can be seen as blocks of code that can be passed around in your code. Blocks of code are often referenced as lambdas in other programming languages. An example from UIKit if for Adding a closure as a target to UIButton and other controls in Swift: let button = UIButton(type: .

What are escaping and non-escaping closures in Swift?

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.

How to escape a closure in Swift 5?

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.

What does @escaping mean in Swift?

In Swift, a closure marked with @escaping means the closure can outlive the scope of the block of code it is passed into. In a sense, @escaping tells the closure to “stay up even after the surrounding function is gone”.

What is an example of a escaping closure in JavaScript?

For example: The someFunctionWithEscapingClosure (_:) function takes a closure as its argument and adds it to an array that’s declared outside the function. If you didn’t mark the parameter of this function with @escaping, you would get a compile-time error.


4 Answers

Consider this class:

class A {
    var closure: (() -> Void)?
    func someMethod(closure: @escaping () -> Void) {
        self.closure = closure
    }
}

someMethod assigns the closure passed in, to a property in the class.

Now here comes another class:

class B {
    var number = 0
    var a: A = A()
    func anotherMethod() {
        a.someMethod { self.number = 10 }
    }
}

If I call anotherMethod, the closure { self.number = 10 } will be stored in the instance of A. Since self is captured in the closure, the instance of A will also hold a strong reference to it.

That's basically an example of an escaped closure!

You are probably wondering, "what? So where did the closure escaped from, and to?"

The closure escapes from the scope of the method, to the scope of the class. And it can be called later, even on another thread! This could cause problems if not handled properly.

By default, Swift doesn't allow closures to escape. You have to add @escaping to the closure type to tell the compiler "Please allow this closure to escape". If we remove @escaping:

class A {
    var closure: (() -> Void)?
    func someMethod(closure: () -> Void) {
    }
}

and try to write self.closure = closure, it doesn't compile!

like image 93
Sweeper Avatar answered Oct 16 '22 09:10

Sweeper


I am going in a more simpler way.

Consider this example:

func testFunctionWithNonescapingClosure(closure:() -> Void) {
        closure()
}

The above is a non-escaping closure because the closure is invoked before the method returns.

Consider the same example with an asynchoronous operation:

func testFunctionWithEscapingClosure(closure:@escaping () -> Void) {
      DispatchQueue.main.async {
           closure()
      }
 }

The above example contains an escaping closure because the closure invocation may happen after the function returns due to the asynchronous operation.

 var completionHandlers: [() -> Void] = []
 func testFunctionWithEscapingClosure(closure: @escaping () -> Void) {
      completionHandlers.append(closure)
 }

In the above case you can easily realize the closure is moving outside body of the function so it needs to be an escaping closure.

Escaping and non escaping closure were added for compiler optimization in Swift 3. You can search for the advantages of nonescaping closure.

like image 24
Anish Parajuli 웃 Avatar answered Oct 16 '22 09:10

Anish Parajuli 웃


I find this website very helpful on that matter Simple explanation would be:

If a closure is passed as an argument to a function and it is invoked after the function returns, the closure is escaping.

Read more at the link I passed above! :)

like image 17
stan Avatar answered Oct 16 '22 08:10

stan


By default the closures are non escaping. For simple understanding you can consider non_escaping closures as local closure(just like local variables) and escaping as global closure (just like global variables). It means once we come out from the method body the scope of the non_escaping closure is lost. But in the case of escaping closure, the memory retained the closure int the memory.

***Simply we use escaping closure when we call the closure inside any async task in the method, or method returns before calling the closure.

Non_escaping closure: -

func add(num1: Int, num2: Int, completion: ((Int) -> (Void))) -> Int {
    DispatchQueue.global(qos: .background).async {
        print("Background")
        completion(num1 + num2) // Error: Closure use of non-escaping parameter 'completion' may allow it to escape
    }
    return num1
}

override func viewDidLoad() {
    super.viewDidLoad()
    let ans = add(num1: 12, num2: 22, completion: { (number) in
        print("Inside Closure")
        print(number)
    })
    print("Ans = \(ans)")
    initialSetup()
}

Since it is non_escaping closure its scope will be lost once we come out the from the 'add' method. completion(num1 + num2) will never call.

Escaping closure:-

func add(num1: Int, num2: Int, completion: @escaping((Int) -> (Void))) -> Int {
    DispatchQueue.global(qos: .background).async {
        print("Background")
        completion(num1 + num2)
    }
    return num1
}

Even if the method return (i.e., we come out of the method scope) the closure will be called.enter code here

like image 10
DEEPAK KUMAR Avatar answered Oct 16 '22 09:10

DEEPAK KUMAR