Differenceand Purpose of Auto and Escaping Closure in Swift

What is the difference and purpose of auto and escaping closure in Swift?

I didn't get the concept of autoclosure closure.

The autoclosure allows a function to wrap an expression in a closure so that it can be executed later or not at all.

A good example of the use of an autoclosure is the short-circuit behavior that happens with ||.

Consider this example:

func willCrash() -> Bool {
fatalError()
return true
}

let good = true

if good || willCrash() {
print("made it")
}

Output:

made it

The || operator uses short-circuit evaluation: The left-hand side (lhs) is evaluated first, and the right-hand side (rhs) is evaluated only if lhs evaluates to false.

So, how is that implemented? Well, || is simply a function that takes two arguments that each evaluate to a Bool and || combines them to return a Bool. But in the normal calling scheme of a function in Swift, the arguments are evaluated before the function is called. If || was implemented in the obvious way:

func ||(lhs: Bool, rhs: Bool) -> Bool {
return lhs ? true : rhs
}

it would crash because of the execution of willCrash() before || was called. So || employs autoclosure to wrap the second statement in a closure so that it can delay evaluation until it is inside the || function. If the first statement (which is evaluated before || was called) is true then the result of the || is true and the closure is not called thus avoiding the crash in this example.

Here is the definition of ||:

static func ||(lhs: Bool, rhs: @autoclosure () throws -> Bool) rethrows -> Bool

Description Performs a logical OR operation on two Bool values. The logical OR operator (||) combines two Bool values and returns true
if at least one of the values is true. If both values are false, the
operator returns false.

This operator uses short-circuit evaluation:
The left-hand side (lhs) is evaluated first, and the right-hand side
(rhs) is evaluated only if lhs evaluates to false.

Ignoring the throws/rethrows which is another topic, the implementation of || becomes:

func ||(lhs: Bool, rhs: @autoclosure () -> Bool) -> Bool {
return lhs ? true : rhs()
}

and rhs() is only called when lhs == false.

What is difference between @noescape, @escaping and @autoclosure?

The most important difference is between @escaping and @noescaping (there is no such keyword in Swift 3!). When a closure is marked as @noescape, you can be sure that the closure won't be retained by the method (e.g. to perform an asynchronous call), therefore you don't have to worry about ownership cycles (there are some other minor benefits).

@escaping closures can be saved or called sometimes in the future therefore you have to be sure to handle ownership (e.g. [weak self]) correctly.

For @autoclosure see How to use Swift @autoclosure . In short, it allows you to skip braces around the closure in some situations.

The default (when not specified) is @noescaping in Swift 3 (see rationale). They keyword does not actually exist anymore. There is only @escaping.

In Swift, What is the difference between (()-()) and @escaping () - Void?

Quick Excerpt


Escaping Closures
A closure is said to escape a function when the closure is passed as an argument to the function, but is called after the function returns. When you declare a function that takes a closure as one of its parameters, you can write @escaping before the parameter’s type to indicate that the closure is allowed to escape.

One way that a closure can escape is by being stored in a variable that is defined outside the function. As an example, many functions that start an asynchronous operation take a closure argument as a completion handler. The function returns after it starts the operation, but the closure isn’t called until the operation is completed—the closure needs to escape, to be called later. For example:

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

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.”

“let alsoIncrementByTen = incrementByTen
alsoIncrementByTen()
// returns a value of 50

incrementByTen()
// returns a value of 60

The example above shows that calling alsoIncrementByTen is the same as calling incrementByTen. Because both of them refer to the same closure, they both increment and return the same running total.

Escaping Closures
A closure is said to escape a function when the closure is passed as an argument to the function, but is called after the function returns. When you declare a function that takes a closure as one of its parameters, you can write @escaping before the parameter’s type to indicate that the closure is allowed to escape.

One way that a closure can escape is by being stored in a variable that is defined outside the function. As an example, many functions that start an asynchronous operation take a closure argument as a completion handler. The function returns after it starts the operation, but the closure isn’t called until the operation is completed—the closure needs to escape, to be called later. For example:

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

The someFunctionWithEscapingClosure(_:) function takes a closure as its argument and adds it to an array that’s declared[…]”


Excerpt From: Apple Inc. “The Swift Programming Language (Swift 5.2).” Apple Books. https://books.apple.com/us/book/the-swift-programming-language-swift-5-2/id881256329


In other words, @escaping closures require the use of self, if you are trying to reference methods within the current object. i.e. Completion Closures of SKActions are a really good example of this.

And non escaping closure's don't require the use of self. Because they weren't used to reference an object.

I am little bit confused about escaping closure

I want to know that completion handler and escaping closure are same
or what?

Completion and Error Handlers are related to a process that should be done after executing -and returning- a chunk of code (function):

Completion handlers are callbacks that allow a client to perform some
action when a framework method or function completes its task.
https://developer.apple.com/library/content/featuredarticles/Short_Practical_Guide_Blocks/

which are -logically- representation of escaping closures.

So, referring to the escaping closures definition, a completion handler is by default is an escaping closures.

For reviewing example(s), you might want to check this answer. Also, if you think that you need more description about what is the escaping closure, you might wan to check this Q&A.

How Swift autoclosure Delaying works?

Your code snippets cannot be a good example of delaying. If you want to find what delaying means, you need to compare @autoclosure to non-closure.

An example of non-closure:

enum LogLevel: Int {
case debug, warning, error
}

extension LogLevel: Comparable {
static func < (lhs: LogLevel, rhs: LogLevel) -> Bool {
return lhs.rawValue < rhs.rawValue
}
}

var currentLogLevel: LogLevel = .error
func outputLogNonClosure(_ level: LogLevel, message: String) {
if level >= currentLogLevel {
print(message)
}
}

outputLogNonClosure(.debug, message: "Debug message")
//output nothing, calling cost can be acceptable.

Sometimes, you may need to pass a result of complex calculation to generate the error message.

func getMyStringSuperHeavy() -> String {
var result = ""
do {
print("Assume doing very complex calculation to get Debug message...")
result = "Debug message"
}
return result
}

outputLogNonClosure(.debug, message: getMyStringSuperHeavy())
//->Assume doing very complex calculation to get Debug message......
//The result of `getMyStringSuperHeavy()` is not used, but `getMyStringSuperHeavy()` is called.

In the latter code above, you may want getMyStringSuperHeavy() not to be evaluated when the output is not needed.


An example of autoclosure:

func outputLogAutoClosure(_ level: LogLevel, message: @autoclosure ()->String) {
if level >= currentLogLevel {
print(message())
}
}

outputLogAutoClosure(.debug, message: "Debug message")
//output nothing, calling cost can be acceptable.

outputLogAutoClosure(.debug, message: getMyStringSuperHeavy())
//output nothing, calling cost can be acceptable as well.

In the last code, the evaluation of getMyStringSuperHeavy() is delay ed till the value is actually used.


Generally the calculation cost of an argument declared as arg: AType cannot be omitted, but the value may not be needed in some cases, you can change it to arg: @autoclosure ()->AType, and use arg() when its value really is needed.

(@autoclosure can be used in some other cases, but you compare arg: AType to arg: @autoclosure ()->AType in every case.)

Escaping Closures in Swift

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!

Is there a way to nullify a escaping closure without calling it?

In your example, the closure is in fact not escaping, since you're not assigning it to anything outside the function, so there's no need to nullify:

func should(completion: () -> Void) {
if !something {
completion()
}
}

But if it was escaping, say by assigning it to a property, then you could nullify the property to release it:

class Foo {
let fn: (() -> Void)?

func should(completion: @escaping () -> Void) {
fn = completion
}

func executeAndRelease() {
fn?()
fn = nil
}
}

How to use Swift @autoclosure

Consider a function that takes one argument, a simple closure that takes no argument:

func f(pred: () -> Bool) {
if pred() {
print("It's true")
}
}

To call this function, we have to pass in a closure

f(pred: {2 > 1})
// "It's true"

If we omit the braces, we are passing in an expression and that's an error:

f(pred: 2 > 1)
// error: '>' produces 'Bool', not the expected contextual result type '() -> Bool'

@autoclosure creates an automatic closure around the expression. So when the caller writes an expression like 2 > 1, it's automatically wrapped into a closure to become {2 > 1} before it is passed to f. So if we apply this to the function f:

func f(pred: @autoclosure () -> Bool) {
if pred() {
print("It's true")
}
}

f(pred: 2 > 1)
// It's true

So it works with just an expression without the need to wrap it in a closure.



Related Topics



Leave a reply



Submit