swift--Closure Package

Source: Internet
Author: User
Tags closure



Closures are self-contained function blocks that can be passed and used in code. Swift closures are similar to blocks in C and OC, with Lambdas in other languages.



Closures can fetch and store any constants and variables that are defined inside the closure, which is called closed constants and variables, and Swift handles all of the captured memory management for you.



Note: If you are unfamiliar with the concept of capture, don't worry, the details will be explained in capture value.






The global methods and nested methods that have been introduced in function are in fact a special example of closures. Closures is used in one of three forms:


    • A global function is a closure function that has a name but does not capture any value.
    • A nested function is a closure that has a function name and can get a value from their closure function.
    • A closure expression is a closure that is written in lightweight syntax and can capture values from the surrounding environment.


Swift's closure expressions have a clean, clear style that uses optimizations that encourage short, irregular syntax in common scenarios. These optimizations include:



Infer parameters and return value types from context



Implicit return from a single-expression closure



Shorthand parameter name



Post-closing syntax






1. Expressions for closures



Nested functions are a convenient way to name and define a self-package function block as part of a large function. However, it is sometimes useful to write a discontinuous, non-complete declaration and named function structure, especially if you need to use a function as a parameter for one or more functions or methods.



Closure expressions a method of inline closures written in concise, centralized syntax. A closure expression provides some syntax optimizations that allow you to write closures in short forms without losing clarity or intent. The following example of a closure expression illustrates these optimizations by redefining the sorted (by:) function, each of which expresses the same functionality in a more concise manner.



(1) Sorting method



The SWIFT Standard Library provides a sroted (by:) method that sorts an array of known types based on the sort closure output you provide. Once this sort is complete, the sorted (by:) method returns a new array that is sorted in the correct order, with the type and size of the array consistent with the old array. The original array is not modified.



The following example of a closure expression uses the sorted (by:) method to sort an array of strings in alphabetical order. The sort (by:) method accepts a closure that accepts two parameters of the same type as the array contents and returns a bool value to determine whether the first value should appear before or after the second value if the value is sorted. If the first value precedes the second value, the sort end needs to return true, otherwise false will be returned.



This example is to sort the array of strings, and this sort of closure requires a function type (string, string), Bool.



One way to provide a sort closure is to write a function of the normal correct type, passing the function to the parameters of the sorted (by:) method.


let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
func backward(_ s1: String, _ s2: String) -> Bool {
    return s1 > s2
}
var reversedNames = names.sorted(by: backward)
// reversedNames is equal to ["Ewa", "Daniella", "Chris", "Barry", "Alex"]


If the first character (S1) is larger than the second string (S2), the function of backward (_:_:) returns true, stating that the S1 in the sorted array should be in front of the S2.



(2) syntax for closure expressions


{ (parameters) -> return type in
    statements
}


In the syntax of a closure expression, parameters can be input-output parameters, but they cannot have default values. When you name parameter variables, variable arguments can be used, and tuples can also act as arguments to the closure expression.



The mentioned earlier.backward(_:_:) function can be represented by the following method:


reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in
    return s1 > s2
})


Note that the functionbackward(_:_:) statement and inline closure function declares the same argument as the return value type. In this two example, it is written as: (S1:string, s2:string), Bool. However, in an inline function closure expression, both the parameter and the return value are written in curly braces, not outside.



The closure function body begins with the "in" keyword. This keyword indicates that the closure parameter and return value type have been completed, and the closure has just begun.



If the closure is short, it can be written on the same line:


Reversednames = names.sorted (by: {(s1:string, s2:string)-Bool in return s1 > s2})


(2) Infer a type from the context



Because the closure function is passed as a parameter to a method, Swift can infer the type of the parameter and the type of the return value. Sprted (by:) When this method is called in a string array, its arguments must be (string, string)->bool the function type. means that (string, string) and Bool types do not need to be part of the definition of a closure expression. Because all types are inferred, the parentheses that return arrows (-) and parameter names can also be omitted.


Reversednames = names.sorted (by: {S1, s2 in return s1 > s2})


When a closure is passed to a function or method as an inline closure expression, it is usually possible to infer the type of the parameter and the type of the return value.



Of course, if you want to, still make your type clear, if for your code to read up to avoid the ambiguity of the time to encourage people to sit.



(3) return an exact value from a simple closure expression



The closure of simple expressions, omitting the return keyword in their expression declarations, can also return exactly one result.


Reversednames = names.sorted (by: {S1, s2 in S1 > s2})


Here, the function type of the parameter of the sorted (by:) method makes the method very clear that the closure must return a bool value.



(4) Shorten the name of the parameter



Sswift automatically provides a short parameter name for the inline closure, which can be used to reference the value of the closure parameter, named $0, $1, $2, and so on.



If you use these short arguments in a closure expression, you can omit the argument list when you define closures. The number and type of names of short parameters can be inferred from the specified parameter type. The In keyword can also be ignored, because the closure expression is composed entirely of its closed-envelope body.


Reversednames = names.sorted (by: {$ > $})


Here, $0,$1 references the parameters of the first and second string types of closures.



(5) Operation method



There is also a shorter method in the closure closure expression above. Swift's string type defines the ratio operator (>) as an argument with two string types and a method that returns a bool type value. So, you can simply pass a ratio operator, and Swift will automatically infer the string-specific function you want to use with it.


Reversednames = names.sorted (by: >)


For more information on how to do this, see " Operator Methods".






2. Trailing closures



If you need to pass a closure expression to a function as the last parameter of the function, and the closure expression is long, you can replace it with a trailing closure. The trailing closure is written after the parentheses of the function call, but it is still the parameter of the function. When you use the trailing closure syntax, you do not need to write the parameter label of the closure as part of the function call.


func someFunctionThatTakesAClosure(closure: () -> Void) {
    // function body goes here
}
 
// Here‘s how you call this function without using a trailing closure:
 
someFunctionThatTakesAClosure(closure: {
    // closure‘s body goes here
})
 
// Here‘s how you call this function with a trailing closure instead:
 
someFunctionThatTakesAClosure() {
    // trailing closure‘s body goes here
}


In the closure expression Syntax section of the string sorting closure, you can write the sroted (by:) method outside the parentheses as a trailing closure:


Reversednames = names.sorted () {$ > $}


If a closure expression is the only parameter of a function or method, and the expression you provide as a trailing closure, you do not need to write () after the function and method name when you call this method.


Reversednames = names.sorted {$ > $}


Trailing closures are useful when closures are long enough to write a line in an inline function. For example, the array type of Swift has a map (_:) method, which uses a closure expression as the only parameter. Each element in the array is called once for the closure, and an alternative value (possibly other types) is returned for each element. The nature of the map and the type of return value are left to the closure to specify.



After a closure is required for each element of an array, the map (_:) method returns an array containing all the new mapped values, in the same order as the original data.



Here is the explanation, if you use a map (_:) method with a trailing closure to turn the int array into an array of string characters. Create a new array of ["Onesix '," Fiveeight "," Fiveonezero "] with the array [16,58,510].


let digitNames = [
    0: "Zero", 1: "One", 2: "Two",   3: "Three", 4: "Four",
    5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine"
]
let numbers = [16, 58, 510]


The code above creates a mapping of the dictionary types for numbers and numbers in English. A numeric array is also defined for conversion to a string.



You can now use the numbers array to create an array of string-type values, passing in a closure expression to the trailing closure of the array map (_:) method.


let strings = numbers.map { (number) -> String in
    var number = number
    var output = ""
    repeat {
        output = digitNames[number % 10]! + output
        number /= 10
    } while number > 0
    return output
}
// strings is inferred to be of type [String]
// its value is ["OneSix", "FiveEight", "FiveOneZero"]


Each element of the array in the map (_:) method invokes a closure expression. You do not need to set the type of the closure input parameter, that is, number, because this type can be inferred from the value of the array element.



In this example, the variable number is the value initialized with the closure parameter, so this value can be modified in the closure. (The parameters of methods and closures are always constants). The closure expression also formulates a return type string, in order to indicate that the type is sorted in the array of mapped outputs.



When this closure expression is called, an output string is constructed. It calculates the last digit (number% 10) by using the remaining operators, and then uses this number in the Digitnames dictionary to find out if there is a matching string.



Note: The call to the subscript of the Digitnames dictionary is followed by an exclamation point (!), because the dictionary sub-script returns an optional value to indicate that the dictionary lookup fails if the key does not exist. In the example above, you can guarantee that number% 10 is always a valid subscript key for a numeric dictionary, so use an exclamation mark to force the decompression of a string value stored in an optional return value of the subscript.



The string obtained from the Digitnames dictionary is added in front of the output, which effectively constructs the converted string of numbers.



The variable number is divisible by 10, because it is an integer, it is round in division, so 16 becomes 1,58 5,510 becomes 51.



This process has been repeatedly known that this number equals 0, while the output string is returned from the closure and added to the map (_:) The output array of the method.



In the trailing closure syntax mentioned above, when the function's closure is provided, the closure function can be encapsulated, and the entire closure should not be encapsulated within the () of the map (_:) method.



3. Capturing values



A closure can get a constant and a variable based on the context it defines. Closures can reference and modify the values of these constants and variables in its closures, even when the scope of the initially defined constants and variables no longer exists.



In Swift, the simplest form of closure in which a value can be captured is a nested function, and another method is written in the closure body. A nested function can get the parameters of the function outside of it, and can get the constants and variables defined by these outer functions.



This example is called the Makeincrementer function, which contains a nested function Incrementer. This nested function incrementer () function can get two values runningtotal and amount from the context. After the two values are obtained, Makeincrementer returns Incrementer as a closure, incremented each time it is called.


func makeIncrementer(forIncrement amount: Int) -> () -> Int {
    var runningTotal = 0
    func incrementer() -> Int {
        runningTotal += amount
        return runningTotal
    }
    return incrementer
}


The return type of Makeincrementer is ()->int, which means that it returns a function instead of a value. The returned function has no arguments and returns an int value each time it is called.



The Makeincrementer (forincrement:) function defines a runningtotal integer variable that stores the total number of running values in the Incrementer and returns. This integer is initialized to 0.



The Makeincrementer (forincrement:) function has a parameter of type int, the parameter label is Forincrement, and the parameter name is amount. When calling Incrementer this method, the incoming parameter specifies how much runningtotal is incremented each time it returns. A nested function is defined in the Makeincrementer function called Incrementer, and the function of this nested function is to increase. This nested function simply adds the value of the amount to the RunningTotal and returns the result of the addition.



It would be strange to consider this nested function separately:


func incrementer() -> Int {
    runningTotal += amount
    return runningTotal
}


The Incrementer () function does not have any arguments, and it still references runningtotal and amount in the closed envelope. It captures the runninttotal and Amoun values from the context function and uses them in their own function body. Obtaining a value by reference ensures that runningtotal and amount do not disappear when the makeincrementer call finishes, and the next time the Incrementer function call is ensured, RunningTotal is valid.



Attention:



As an optimization, if the value is not changed by a closure, Swift can capture and store a copy of the value, and if the value does not mutate after the closure has been created, you can save a copy of the value.



Swift also handles all memory management of variables that are processed when they are no longer needed.



The following is a call to Makeincrementer:


Let Incrementbyten = Makeincrementer (forincrement:10)


This example sets a constant named Incrementbyten to refer to an increment function, which, each time it is called, adds 10 variables to its runningtotal variable if the function is called multiple times, the result is as follows:


incrementByTen()
// returns a value of 10
incrementByTen()
// returns a value of 20
incrementByTen()
// returns a value of 30


If you create an increment function again, it will have its own storage reference to a new, independent runningtotal variable:


let incrementBySeven = makeIncrementer(forIncrement: 7)
incrementBySeven()
// returns a value of 7


Once again, calling Incrementerbyten will continue to increase its own runningtotal variable without affecting the Incrementbyseven variable.


Incrementbyten ()//Returns a value of 40


Note: If you set a closure property on a class object, and the closure is obtained by referencing the instance or its members, you create a strong circular reference in the closures and instances.






4. Closures are reference types



In the above example, Incrementbyseven and Incrementbyten are constants, but the reference to the closure constant can still increase the value of the captured RunningTotal variable. This is because both the function and the closure are reference types.



Whenever you assign a function or a closure to a constant or variable, you actually point the constant and the variable to a function or a closure. In the above example, incrementbyten refers to a constant, not the contents of a closure.



It means that if you assign a closure to two different variables or constants, the two constants or variables point to the same closure:


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


5. Escape closure



If a closure is passed in as a function parameter, the closure is called the escape function, but it is called when the function returns. When you declare a function, one of the arguments of the function is a closure, you can write "@escaping" in front of the parameter type to indicate that the closure is allowed to flee.



There is also a closure that can escape by storing it on a variable that is defined outside of the function. For example, many functions that initiate asynchronous operations take a closure parameter as the completion handler. This function returns at the beginning of the operation, but the closure is not called to know that the operation is complete-the closure needs to be escaped and called later. For example:


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


The Somefunctionwithescapingclosure (_:) function parameter type is a closure and adds it to an array outside the function. If you do not mark "" on the parameters of the function@escaping, you will be prompted to edit the error.



Controlling closures through @escape means that you must explicitly refer to self-display in closures. For example, in the following code, the closure passed to Somefunctionwithescapingclosure (_:) is an escape closure, which means that it needs to explicitly refer to self. In contrast, a closure passed to Somefunctionwithnonescapingclosure (_:) is a nonescaping closure, which means that it can refer to self-hinting.


func someFunctionWithNonescapingClosure(closure: () -> Void) {
    closure()
}
 
class SomeClass {
    var x = 10
    func doSomething() {
        someFunctionWithEscapingClosure { self.x = 100 }
        someFunctionWithNonescapingClosure { x = 200 }
    }
}
 
let instance = SomeClass()
instance.doSomething()
print(instance.x)
// Prints "200"
 
completionHandlers.first?()
print(instance.x)
// Prints "100"


6. Automatic closure



An automatic closure is a closure that is automatically created as an expression used as a function parameter. When called, it is not necessary to pass in any arguments, returning the value of the expression. This syntactic convenience allows you to omit curly braces around the parameters of the function, rather than explicit closur, rather than writing a normal expression.



It is normal to call functions through automatic closures, but it is not uncommon to implement this method. For example:assert(condition:message:file:line:)函数用自动闭包作为condition和message的参数,condition这个参数只有在编译debug时才调用,而message只有在condition为false时才调用。



An automatic closure allows you to delay the evaluation because the internal code will not execute until you call this closure. Latency assessment is useful for code that has side effects or is computationally expensive because it allows you to control when the code is evaluated. The following code shows a closure if the delay is evaluated.


func someFunctionWithNonescapingClosure(closure: () -> Void) {
    closure()
}
 
class SomeClass {
    var x = 10
    func doSomething() {
        someFunctionWithEscapingClosure { self.x = 100 }
        someFunctionWithNonescapingClosure { x = 200 }
    }
}
 
let instance = SomeClass()
instance.doSomething()
print(instance.x)
// Prints "200"
 
completionHandlers.first?()
print(instance.x)
// Prints "100"


Although the first element of the Customersinline array has been removed from the inside of the closure, the elements of the array do not really remove the coarse fat closures that are really called. If the closure is not called, the closure expression is not evaluated, which means that the elements in the array are never removed. Note that the Customerprovider type is not a String but ()->string--a function that has no arguments but has a return value.



You can get the same delay effect when you pass in a closure as a function parameter:


// customersInLine is ["Alex", "Ewa", "Barry", "Daniella"]
func serve(customer customerProvider: () -> String) {
    print("Now serving \(customerProvider())!")
}
serve(customer: { customersInLine.remove(at: 0) } )
// Prints "Now serving Alex!"


The serve (Customer:) function above uses this exact closure, which returns a user's name. The following version of Serve (Customer:) performs the same operation, but does not use an exact closure, but instead uses a type with an@autoclosure的auto-closure parameter as the parameter. Now you can call this function with a string as the closure.



Note: Excessive use of automatic closures can make your code look laborious.



If you want to use an automatic closure to allow escape, use and@autoclosure@escaping属性。


// customersInLine is ["Barry", "Daniella"]
var customerProviders: [() -> String] = []
func collectCustomerProviders(_ customerProvider: @autoclosure @escaping () -> String) {
    customerProviders.append(customerProvider)
}
collectCustomerProviders(customersInLine.remove(at: 0))
collectCustomerProviders(customersInLine.remove(at: 0))
 
print("Collected \(customerProviders.count) closures.")
// Prints "Collected 2 closures."
for customerProvider in customerProviders {
    print("Now serving \(customerProvider())!")
}
// Prints "Now serving Barry!"
// Prints "Now serving Daniella!"


swift--Closure Package


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.