Automatic closure of Swift (@autoclosure, @noescape, @escape 、??)

Source: Internet
Author: User
Tags closure generator

@autoclosure (Automatic closure) 1: Automatic closure, as the name implies is an automatically created closure, used to wrap the function parameters of the expression, can be said to be a simple syntax.  2: Automatic closures do not accept any parameters and are invoked to return the value of the expression being wrapped in it.  3: The benefit of automatic closures is that you can delay the evaluation because the code snippet will not be executed until you call the closure, so you can control when the code executes. 4: Declarations containing autoclosure characteristics also have noescape characteristics, and the default is a non escape closure unless an optional parameter escaping is passed. If this argument is passed, the closure can be operated outside the closure in the form of: please use @autoclosure ( Escaping).
Let's look at a simple example: for example, we have a method that accepts a closure and prints when the result of the closure is true:

Func printiftrue (predicate: ()-> Bool) {
    if predicate () {
       print ("The result is true")
    }

// 1 Direct Call Method
Printiftrue {()-> Bool
    in return 2 > 1
}
//2 closures within parentheses
printiftrue ({return 2 > 1}) C11/>//3: Using the tail closure method, the closure is
printiftrue () {return 2 > 1}
/4: The use of closures in Swift can be simplified, in which case we can omit Return, written by:
Printiftrue ({2 > 1})
/5: This is a step closer because the closure is the last parameter, so you can use the trailing closure (trailing closure) to get the braces out, Then omit the parentheses and change to:
printiftrue{2 > 1}


But either way, the expression is not clear enough to look uncomfortable. So @autoclosure on the stage. We can change the method parameters and precede the parameter names with the @autoclosure keyword:


Func printiftrue (@autoclosure predicate: ()-> Bool) {
    if predicate () {
        print ("The result is true")
    }

printiftrue (2 > 1)
//directly to the call, Swift will automatically convert the expression 2 > 1 to ()-> Bool. So we get a formula that is simple and has a clear meaning.

If you have more than one closure, you have an advantage, and @autoclosure is a parameter that can be decorated in any location:


Func printinformation (@autoclosure predicate1: ()-> bool, @autoclosure predicate2: ()-> bool) {
    
    if predicate1 ( ) && Predicate2 () {
        print (' The ' is True ')
    }else{
        Print ("The result is false")
    }
}< C12/>printinformation (3 > 2, predicate2:4 > 1)


@noescape and @escape
For the Autoclosure property, there are 2 related properties to understand. Which is @noescape and @escape. These 2 properties are all used to modify the closure. @noescape means a @escape closure, while the opposite is true. By default, closures are @escape. Indicates that the closure can also be invoked by other closures. Like our usual asynchronous operations .

Func Executeasyncop (asyncclosure: ()-> ())-> Void {
    Dispatch_async (dispatch_get_global_queue Queue_priority_default, 0)) {
        asyncclosure ()
    }
}

Where Asyncclosure is invoked in the closure of the Dispatch_async to complete the asynchronous operation. Because closures are @escape by default, the code above can be run. But when we add the @noescape attribute in front of the asyncclosure, the compiler complains:

Func Executeasyncop (@noescape asyncclosure: ()-> ())-> Void {
    Dispatch_async ( Dispatch_queue_priority_default, 0)) {
        asyncclosure ()
    }
}
error:closure use of @noescape parameter ' Asyncclosure ' may allow it to escape asyncclosure ()

@noescape attribute is introduced in Swift1.2, restricting incoming closure parameter calls to functions that are called, improving performance, and labeling the closure as @noescape allows you to implicitly reference self in the closure.
Many methods in the SWIFT standard library use the @noescape attribute, such as array-corresponding methods Map,filter and reduce:

Func map<t> (@noescape Transform: (Self.Generator.Element)-> t)-> [T]

func filter (@noescape Includeelement: (Self.Generator.Element)-> Bool)-> [Self.Generator.Element]

func reduce<t> (initial : T, @noescape combine: (t, Self.Generator.Element)-> t)-> t


note, however, that the @noescape is default in Swift3 and recommends that @noescape be removed. In fact, @noescape has been abandoned in swift3 and will not be used in the future, and @escape changed to @escaping.
Let's take a look at @escaping .
In Swift we can define a function that accepts functions as arguments, and when invoked, it is common to use closures to pass this parameter, as follows:

Func doWork (Closure: ()-> ()) {
    closure ()
    print ("End")
}

doWork {
    print ("DoWork")
}
DoWork
//end
This simplest form of closure in fact also hides the assumption that the contents of the closure in the parameter will be completed before the DoWork return, and the results can be seen. In other words, the call to closure is synchronous, so what if it is asynchronous. Everyone should guess the result, asynchronous is not immediately return.
So let's change the code, put the closure in a dispatch, let it be called after Doworkasync returns, and we need to add @escaping tag to the closure type to show that the closure is going to "escape" the Method:

Func Doworkasync (Closure: @escaping ()-> ()) {
    DispatchQueue.main.async {
        closure ()
    }
    Print (" End ")
}

doworkasync {
   print (" DoWork ")
}

//end
//dowork

When using closures to invoke this two method, there are also some differences in behavior. We know that closures are the variables that can be captured. For a closure with no escape behavior in the DoWork parameter, because the closure scope does not exceed the function itself, we do not have to worry about holding self in the closure. But the Doworkasync that accepts @escaping is different. Because you need to ensure that the members in the closure are still valid, Swift will force us to explicitly write self if the self and its members are referenced within the closure. We can compare the differences between the following two use cases:

Class Person {
    var name = "Jack"
    
    func method1 () {
        doWork {
            print ("name = \ (name)")
        }
        name = "Rose"
    }
    
    Func method2 () {
        Doworkasync {
            print ("name = \ (self.name)")
        }
        name = "Rose"
    }
    
    func method3 () {
        Doworkasync {[weak self] in
            print ("name = \" String (describing:self?). name))
        }
        name = ' Rose '}
    }
Obviously, the caller in METHOD1 does not need to consider the holding of self.name, which is quite straightforward to use. The print output for name is the original value. In Method2, because closures can escape, Swift forces us to write self as a reminder, and we need to consider the ownership of self. In this example, we let the closure hold self, and the printed value is rose after the last assignment to name. If you do not use self, an error occurs:


If we do not want to hold self in the closure, we can use [weak self] to handle it, which is the Method3 method

If you define a method of accepting @escaping as a parameter in a protocol or a parent class, the corresponding method must also be declared as a subclass of the implementation protocol and type or the parent class, otherwise two methods will be considered to have different function signatures, such as:

Protocol P {
    func work (b: @escaping ()-> ())
}
//Normal
class C:p {
    func work (b: @escaping ()-> ( )) {
        
    }
}
If we do not use @escaping, an error occurs:


?. Operator


In Swift, there is a very useful operator that can be used to quickly determine the condition of a nil, which is??。 This operator can determine the input and return its value when the value on the left is a nil Optional value, and return to the right when the left side is nil, for example:

var level:int?
var startlevel = 1

The above example results in 1, the example we did not set the level, so the last Startlevel is assigned to CurrentLevel.?? The operator actually has two forms, as follows:
Func?? <T> (Optional:t, DefaultValue: @autoclosure () throws-> T?) rethrows-> t?
Func?? <T> (Optional:t, DefaultValue: @autoclosure () throws-> T) rethrows-> t
Here our input satisfies the latter, although on the surface startlevel is only an Int, but in fact it is automatically encapsulated as ()->int when used, we may simply guess the internal implementation, as follows:

Func?? <T> (Optinal:t, DefaultValue: @autoclosure ()-> t)-> t {
    switch optinal {case
    . Some (let value): 
  return value Case
    . None: Return
        defaultvalue ()
    }
}
is actually the application of the optional value.



Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.