Closure (Closures) is a separate block of function code that can be passed and used in code. The closures in Swift are similar to the anonymous functions in C and objective-c and in other programming languages.
Closures can capture and store any defined constants and variable references within the context. Because of the closeness of these constants and variables, it is named "Closure" (Closures). Swift can manage memory for all the references you can capture.
Note
If you are unfamiliar with "capture (capturing)", please do not worry, you can refer to capturing values (capture value).
global functions and nested functions have been introduced in functions (functions), which are actually special closure functions
Global functions are closures that feature a function name but do not capture any values.
Nested functions are closures, characterized by a function name, and can capture values in functions that it encloses.
A closure expression is a closure, characterized by the absence of a function name and the use of lightweight syntax to capture values in the context in which it surrounds.
Swift's closure expression has a clean, clear style and is often optimized for encouraging short, neat syntax. These optimizations include:
Inference parameters and return value types originate from context
Implicit return from a single expression closure
Simple parameter name
Trailing closure syntax
1, the closure of the package expression
Nested functions have been introduced in nested functions (nested functions) and are a convenient way to name and define self-contained blocks of code, however, sometimes it is useful to write short functional constructors that do not require complete function declarations and function names. Especially if you need to call one or more of the parameters of a function.
A closure expression is a way to write an inline closure, which is concise and compact. A closure expression provides several semantic optimizations to be programmed in the simplest form without the need for a large amount of declaration or intent. The following is a few improvements with the same sort function, each of which is more concise, to illustrate the optimization of the closure expression.
Sort function
Swift's standard function library provides a function called sort, which sorts the values of the array data of a given type by a closure function sorted based on the output type. Once you finish sorting, you return a new array of the same size, same data type, and the same element as the previous array, and the elements of the array are placed in the correct order.
The closure expression examples below use the sort function to sort a array of String values in reverse alphabetical R. Here's the initial array to be sorted:
The following closure expressions are described by alphabetical order of string values through the sort function, which is the initialized array to be sorted.
Copy Code code as follows:
Let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
The sort function requires two parameters:
An array of known value types
A closure function that receives two parameters, both of which are of the same data type as the array element. And
Returns a bool indicating whether the first argument should be before or after the second argument.
This example is a set of sorted string values, so you need to sort the enclosing type of function (string, string)-> Bool.
One way to construct a sort closure is to write a normal function that conforms to its type requirements: backwards, and passes its return value as the second argument to the sort function:
Copy Code code as follows:
Func Backwards (s1:string, s2:string)-> Bool {
return s1 > S2
}
var reversed = sort (names, backwards)
Reversed is equal to ["Ewa", "Daniella", "Chris", "Barry", "Alex"]
If the backwards function argument S1 greater than S2, returns a true value indicating that S1 should appear before S2 in the new array sort. "Greater than" in the character indicates "appear alphabetically". This means that the letter "B" is greater than the letter "A" and that the string "Tom" is greater than the string "Tim". It will be sorted alphabetically, Barry will be ranked after Alex, and so on.
But this is a rather lengthy way, essentially just a simple single expression function: (a > B). In the following example, we use a closed expression to construct an inline sort closure that is more efficient than the example above.
Closure-expression Syntax
The closed expression syntax has the following general construction form:
Copy Code code as follows:
{(parameters)-> return type in
Statements
}
The closure expression syntax can use constant arguments, variable arguments, and inout types as arguments, but no default values are provided. If you need to use a variable parameter, you can put the variable argument at the end, and the tuple type can also be used as a parameter and a return value.
The following example shows the closure expression constructor code for the above backwards function
Copy Code code as follows:
Reversed = sort (names, {(s1:string, s2:string)-> Bool in
return s1 > S2
})
Note that the parameter and return value type of the declaration inline closure is identical to the backwards function type declaration. In both of these ways, the type of (s1:string, s2:string)-> bool is written. In an inline closure expression, however, functions and return value types are written in curly braces, not outside curly braces.
The function body part of the closure is introduced by the keyword in. This keyword indicates that the closure's parameter and return value type definitions have been completed and the closure function body is about to begin.
Because the function of the closure is very short, the backwards function can be abbreviated to a line of coherent code.
Copy Code code as follows:
Reversed = sort (names, {(s1:string, s2:string)-> Bool in return s1 > s2})
You can see that the overall call to the sort function remains unchanged, or that a pair of parentheses contains two parameters that become inline closures, except that the value of the second parameter becomes. And one of the parameters now becomes an inline closure (compared to the backwards version of the code).
Infer types from context
Because the sort closure is passed in as a function parameter, Swift can infer its parameters and the type of the return value. Sort expects the second argument to be a function of type (string, string)-> Bool, so actually string, string, and Bool types do not need to be part of the closure expression definition. Since all types can be inferred correctly, the return arrow (->) and parentheses around the parameter can also be omitted:
Copy Code code as follows:
Reversed = sort (names, {s1, S2 in return s1 > s2})
In practice, the parameters and return value types of closures can be determined by constructing the closure of an inline closure expression as a function parameter, which means that you hardly need to construct any inline closures using the full format.
Similarly, if you want to avoid ambiguity that might exist when reading a function, you can directly specify the type of parameter.
In this sort of function example, the purpose of the closure is clear, that is, the ordering is replaced, and it is safe for the reader to assume that the closure may use a string value because it is assisting in sorting a string array.
Single-line expression closures can omit return
A single-line expression closure can implicitly return the result of a single line expression by hiding the Returns keyword, as in the case of the previous version, which can be rewritten as:
Copy Code code as follows:
Reversed = sort (names, {s1, s2 in S1 > s2})
In this example, the second parameter function type of the sort function explicitly illustrates that the closure must return a Bool type value. Because the closure function body contains only a single expression (S1 > S2), the expression returns the Bool type value, so there is no ambiguity, the return keyword can be omitted.
Abbreviation of parameter name
Swift automatically provides the function of parameter name shorthand for inline functions, and can refer to the closure's parameter values directly through names such as $0,$1,$2.
If you use a parameter name in a closure expression, you can omit its definition in the closure parameter list, and the type of the corresponding parameter name is inferred by the function type. The In keyword can also be omitted because at this point the closure expression is entirely composed of the closure function body:
Copy Code code as follows:
Reversed = sort (names, {$ > $})
In this example, $ and $ represent the parameters of the first and second String types in the closure.
operator functions
The operator function is actually a shorter way to construct the above expression.
Copy Code code as follows:
Reversed = sort (names, >)
See operator functions for more information on operator expressions.
2, trailing closure
If you need to pass a long closure expression as the last argument to the function, you can use the trailing closure to enhance the readability of the function.
The trailing closure is a closure expression that is written outside the function parenthesis (after), and the function supports calling it as the last argument.
Copy Code code as follows:
Func somefunctionthattakesaclosure (Closure: ()-> ()) {
function Body Goes
}
Here's how to call the This function without using a trailing closure:
Somefunctionthattakesaclosure ({
Closure ' s body goes here
})
Here's how to call the This function with a trailing closure instead:
Somefunctionthattakesaclosure () {
Trailing closure ' s body goes here
}
Attention
If the function only needs a closure expression of one parameter, you can even omit () it when you use the trailing closure.
The string sort closure that is the parameter of the sort function in the example above can be rewritten as:
Copy Code code as follows:
Reversed = sort (names) {$ > $}
Trailing closures become very useful when closures are so long that they cannot be written on a single line. For example, Swift's Array type has a map method that gets a closure expression as its unique parameter. Each element in the array calls the closure function once, and returns the value mapped by the element (or a different type of value). The specific mapping method and return value type are specified by the closure.
When supplied to an array closure function, the map method returns a new array containing the mapped values corresponding to the original array one by one.
The following example describes how to use the trailing closure in the map method to convert an Int type array [16,58,510] to an array that contains the corresponding String type ["Onesix", "Fiveeight", "Fiveonezero"]:
Copy Code code as follows:
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 an integer number to map the dictionary between their English names. It also defines an integer array that is ready to be converted to a string.
You can now create the corresponding string version array by passing a trailing closure to the numbers map method. Call Numbers.map when you need to be aware that you do not need to include any parentheses behind the map, because you only need to pass a closure expression, and the closure expression parameter is written in trailing way:
Copy Code code as follows:
Let strings = Numbers.map {
(var number)-> String in
var output = ""
While number > 0 {
Output = digitnames[number% 10]! + Output
Number/= 10
}
Return output
}
Strings is inferred to be of type string[]
Its value is ["Onesix", "Fiveeight", "Fiveonezero"]
The map calls the closure expression for each element in the array. You do not need to specify the type of the closure's input parameter number because it can be inferred by the type of array to map.
The closure number parameter is declared as a variable parameter (the specific description of the variable is referred to constant and Variable Parameters), so it can be modified in the closure function body. The closure expression provides a string for the return value type to indicate that the new array type that stores the mapped value is string.
The closure expression creates a string and returns each time it is invoked. It computes the last digit using the remainder operator (number% 10) and obtains the mapped string using the Digitnames dictionary.
Attention:
The dictionary digitnames followed by an exclamation mark (!), because the dictionary subscript returns an optional value (optional value), indicating that it does not find a failure even if the key does not exist. In the example above, it guarantees that number% 10 can always be a valid subscript key for a digitnames dictionary. Therefore, the exclamation mark can be used to strongly expand (force-unwrap) a String value stored in an optional subscript item.
The string obtained from the Digitnames dictionary is added to the front of the output, and a string version of the number is created in reverse order. (in expression Number 10, if number is 16, return 6,58 returns 8,510 returns 0).
Number variable divided by 10. Because it is an integer, the part of the calculation that is not removed is ignored. So 16 became 1,58 into 5,510 into 51.
The whole process repeats until number/= 10 is 0, when the closure will output the string, and the map function adds the string to the mapped array.
In the example above, the trailing closure syntax neatly encapsulates the specific closure function after the function, instead of wrapping the entire closure within the parentheses of the map function.
3, get the value
Closures can capture (reference/Get) constants and variables within the scope of their definition, and closures can refer to and modify these values, and can be modified and referenced even if the defined constants and variables have ceased to exist. It's cool,
The simplest form in Swift is a nested function, written in the method of another function. Nested functions can capture the parameters of any external function, or you can capture any constants and variables defined in an external function.
Look at this example, a function method is Makeincrementor, this is a nested function, in this function body nested another function method: Incrementor, in this incrementor function body has two parameters: RunningTotal and amount, when the actual operation is passed into the required two parameters, each time the Incrementor function is called returns a RunningTotal value provided to the external makeincrementor use:
Copy Code code as follows:
Func makeincrementor (forincrement amount:int)-> ()-> Int {
var runningtotal = 0
Func incrementor ()-> Int {
RunningTotal + + Amount
Return RunningTotal
}
Return Incrementor
}
And the return type value of the function Makeincrementor we can tell by the ()-> int after the function name that the value returned is an int type. If you want to learn more about the function return type, you can refer to: function Types as returns Types. (Hyperlink jump)
We can see makeincrementor the function body first defines an integer variable: runningtotal, the initial value is 0, and the return value that the Incrementor () function eventually runs is assigned to the integer variable.
The Makeincrementor function () throws a forincrement argument externally to the outside, and once the value enters the function, the function is instantiated instead of the amount. The amount is then added to a new runningtotal and returned by adding the Incrementor function body with integral type constant runningtotal. And the value we return for this main function is the int type, RunningTotal is returned directly as the final value, and the Makeincrementor function () is executed.
The Makeincrementor function () defines a new function-body incrementor within it, which is the value that is passed over from the outside amount In the Incrementor function, a new runningtotal is added to the runningtotal of the reshaping constant,
Looking at the Incrementor function alone, you'll find this function unusual:
Copy Code code as follows:
Func incrementor ()-> Int {
RunningTotal + + Amount
Return RunningTotal
}
Because the Incrementor function does not have any arguments, but it points to runningtotal and amount in its function method, it is obvious that this is the Incrementor function that gets the value of the external function amount, Incrementor cannot modify it, but it can be added to the body's runningtotal to get a new runningtotal value back out.
However, because the runningtotal is added each time it is invoked to change the actual value, the Incrementor function is invoked to load the latest RunningTotal value. Instead of being initialized for the first time 0. And you need to ensure that each runningtotal value is not lost in the Makeincrementor function body until the function is fully loaded. To ensure that the last value is still in the next reference in the function body.
Attention
Swift needs to know exactly when the reference is to be assigned, and you do not need to annotate amount and runningtotal in the Incrementor function. Swift is also responsible for handling how memory should be managed when the function does not need to be runningtotal.
Here's an example of the Makeincrementor function:
Copy Code code as follows:
Let Incrementbyten = Makeincrementor (forincrement:10)
4. Reference type closure
In the above example, Incrementbyseven and Incrementbyten are constants, but these constants can still be modified in the state of the closure. Why? Very simple, because functions and closures are reference types.
When you specify a function or a closure constant/variable, you are actually setting the constant or variable as a reference function. In the example above, it is a closed selection, incrementbyten refers to the constant, not the contents of the enclosing part itself.
This also means that if you assign a closed two different constants or variables, these two constants or variables will refer to the same closure:
Copy Code code as follows:
Let Alsoincrementbyten = Incrementbyten
Alsoincrementbyten ()
Returns a value of 50