Closures are self-contained function modules that can be passed and used in code. They are very similar to blocks in c and oc, and lambdasbas expressions in .net.
Closures can capture and store references to arbitrary constants and variables in their context. The packages that wrap these constants and variables are commonly known as closures. Swift will manage the memory operations involved in the capture process for you. The following are the three manifestations of closures
A global function is a closure with a name but no value
A nested function is a closure that has a name and can capture values in its closed function domain
A closure expression is an unnamed closure written in a lightweight syntax that can capture the value of a variable or constant in its context
Swift's closure expression has a concise style, and encourages grammatical optimization in common scenarios. The main optimizations are as follows:
Use context to infer parameter and return value types
Single-expression closures can omit the return keyword
Parameter name shorthand
Trailing closure syntax
Closure expression
Nested functions are a way to conveniently name and define self-contained code modules in more complex functions. Of course, sometimes it is also useful to write small, function-like structures that are not fully defined and named, especially when dealing with some functions and requiring other functions as parameters to the function. The following examples of closure expressions show the sort function definition and syntax optimization by using several iterations
Sort method
The Swift standard library provides a sort function that sorts the values in an array of known types according to the sort closure you provide. Once sorting is complete, the function returns a new array of the same size as the original array, which contains elements of the same type that have been correctly sorted.
The following closure expression example uses the sort function to sort an array of type String in reverse alphabetical order. The following are the initial array values:
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
The sort function has two parameters:
1. An array of known type values.
2. A closure that takes two parameters of the contents of an array of the same type and returns a boolean value to indicate whether to put the first value before or after the second value when sorting. If the first value should appear before the second value, the closure needs to return true, otherwise it returns false.
This example sorts an array of type String in descending order, so the sort closure needs to be a function of type (String, String)-> Bool.
func backwards (s1: String, _ s2: String)-> Bool {
return s1> s2
}
var reversed = names.sort (backwards)
// reversed is equal to ["Ewa", "Daniella", "Chris", "Barry", "Alex"]
If the first string (s1) is greater than the second string (s2), the backwards function returns true, indicating that s1 should appear before s2 in the new array. The "greater than" in the character means "appears in alphabetical order". This means that the letter "B" is greater than the letter "A", and the string "Tom" is greater than the string "Tim". It will be sorted in reverse alphabetical order, "Barry" will be sorted before "Alex", and so on.
However, this is a rather verbose way, essentially just writing a single expression function (a> b). In the following example, the use of closed expression syntax can better construct an inline sort closure.
Closure expression syntax
technology sharing
Closure expression syntax can use constants, variables, and inout types as parameters, but does not provide default values. Variable parameters can also be used at the end of the parameter list. Tuples can also be used as parameters and return values.
reversed = sort (names, {(s1: String, s2: String)-> Bool in
return s1> s2
})
It should be noted that the inline closure parameter and return value type declarations are the same as the backwards function type declarations. In both methods, they are written as (s1: String, s2: String)-> Bool type. However, in inline closure expressions, the function and return value types are written in braces, not outside the braces.
The function body part of the closure is introduced by the keyword in. This keyword indicates that the parameter and return value types of the closure have been defined and the body of the closure function is about to begin.
Because the function body part of this closure is so short that it can be rewritten into a line of code
reversed = sort (names, {(s1: String, s2: String)-> Bool in return s1> s2})
This shows that the overall call of the sort function remains unchanged, and a pair of parentheses still wraps the entire set of parameters in the function. And one of the parameters is now an inline closure.
Infer type based on context
Because the sorting closure is passed in as a function parameter, Swift can infer the type of its parameters and return value. sort expects the second parameter to be a function of type (String, String)-> Bool, so actually the String, String and Bool types do not need to be part of the definition of the closure expression. Because all types can be correctly inferred, the return arrow (->) and the parentheses around the parameter can also be omitted:
reversed = sort (names, {s1, s2 in return s1> s2})
In any case, when a closure constructed by an inline closure expression is passed as a parameter to a function, you can infer the parameters and return value types of the closure, which means you hardly need to construct any Joint closure.
However, you can also use explicit types. If you want it to avoid the ambiguity that readers may have, this is still worthy of encouragement. In this sorting function example, the purpose of the closure is very clear, that is, the sorting is replaced, and it is safe for the reader to assume that the closure may use string values because it is assisting a string array to sort.
Single-line expression closure can omit return
A single-line expression closure can implicitly return the result of a single-line expression by hiding the return keyword. The example in the previous version can be rewritten as:
reversed = sort (names, {s1, s2 in s1> s2})
In this example, the second parameter function type of the sort function specifies that the closure must return a value of type Bool. Because the body of the closure function contains only a single expression (s1> s2), the expression returns a value of type Bool, so there is no ambiguity here, and the return keyword can be omitted.
Abbreviated parameter name
Swift automatically provides a shorthand function for parameter names for inline functions. You can directly refer to the value of the parameter of the closure by names such as $ 0, $ 1, $ 2, etc.
If you use shorthand for parameter names in closure expressions, you can omit their definition in the closure parameter list, and the type of the corresponding shorthand parameter name will be inferred by the function type. The in keyword can also be omitted, because the closure expression is entirely composed of the closure function body:
reversed = sort (names, {$ 0> $ 1})
Operator function
There is actually a shorter way to write the closure expression in the above example. Swift's String type defines a string implementation about the greater-than sign (>), which allows it to accept two parameters of type String and return a value of type Bool as a function. And this just coincides with the function type required by the second parameter of the sort function. Therefore, you can simply pass a greater-than sign, and Swift can automatically infer that you want to use the greater-than string function implementation:
reversed = sort (names,>)
Trailing closure
If you need to pass a long closure expression as the last parameter to the function, you can use trailing closures to enhance the readability of the function.
The Trailing closure is a closure expression written outside (after) the function brackets. The function supports calling it as the last parameter.
func someFunctionThatTakesAClosure (closure: ()-> ()) {
// Function body part
}
// The following is a function call without trailing closure
someFunctionThatTakesAClosure ({
// The main part of the closure
})
// The following is the function call using trailing closure
someFunctionThatTakesAClosure () {
// The main part of the closure
}
Note: If the function only needs one parameter of the closure expression, when you use trailing closure, you can even omit ().
In the above example, the string sorting closure as a parameter of the sort function can be rewritten as:
reversed = sort (names) {$ 0> $ 1}
When the closure is so long that it can't be written on one line, the Trailing closure becomes very useful. For example, Swift's Array type has a map method, which takes a closure expression as its only parameter. Each element in the array calls the closure function once and returns the value mapped by the element (which can also be a different type of value). The specific mapping method and return value type are specified by the closure.
When provided to the array closure function, the map method will return a new array, which contains the mapped values corresponding to the original array.
The following example shows how to use the trailing closure in the map method to convert an Int type array [16,58,510] to an array containing the corresponding String type ["OneSix", "FiveEight", "FiveOneZero"]
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 above code creates a mapping dictionary between integer numbers and their English names. At the same time, an integer array to be converted to a string is defined.
You can now create a corresponding array of string versions by passing a trailing closure to the map method of numbers. When you need to pay attention to calling numbers.map, you do n’t need to include any parentheses after the map, because you only need to pass the closure expression parameter, and the closure expression parameter is written by trailing:
let strings = numbers.map {
(var number)-> String in
var output = ""
while number> 0 {
output = digitNames [number% 10]! + output
number / = 10
}
return output
}
// strings constants are inferred as arrays of string type, ie String []
// The value is ["OneSix", "FiveEight", "FiveOneZero"]
map calls the closure expression for each element in the array. You don't need to specify the type of the input parameter number of the closure, because it can be inferred by the type of array to be mapped.
The number parameter of the closure is declared as a variable parameter, so it can be modified in the body of the closure function. The closure expression specifies that the return value type is String to indicate that the new array type storing the mapped value is String.
The closure expression creates a string and returns each time it is called. It uses the remainder operator (number% 10) to calculate the last digit and uses the digitNames dictionary to get the mapped string.
Note:
The dictionary digitNames subscript is followed by an exclamation point (!), Because the dictionary subscript returns an optional value, indicating that the search will not fail even if the key does not exist. In the above example, it guarantees that number% 10 can always be used as a valid subscript key for a digitNames dictionary. Therefore, the exclamation point can be used to force-unwrap the String value stored in the optional subscript.
From digitNames dictionary The string obtained in is added to the front of the output, and a string version of the number is created in reverse order. (In the expression number% 10, if the number is 16, it returns 6, 58 returns 8, and 510 returns 0).
The number variable is then divided by 10. Because it is an integer, the undivided part is ignored during the calculation. So 16 becomes 1, 58 becomes 5, and 510 becomes 51.
The whole process is repeated until number / = 10 is 0, then the closure will output the string, and the map function will add the string to the mapped array.
In the above example, the trailing closure syntax neatly encapsulates the specific closure function after the function, instead of wrapping the entire closure in the parentheses of the map function.
Capture
Closures can capture constants or variables in the context of their definition. Even if the original scope defining these constants and variables no longer exists, the closure can still reference and modify these values in the body of the closure function.
The simplest form of closure in Swift is a nested function, which is a function defined in the body of another function. Nested functions can capture all the parameters and defined constants and variables of their external functions.
The following example is a function called makeIncrementor, which contains a nested function called incrementor. The nested function incrementor captures two values from the context, runningTotal and amount. Then makeIncrementor returns the incrementor as a closure. Each time incrementor is called, it will increase the value of runningTotal in increments of amount.
func makeIncrementor (forIncrement amount: Int)-> ()-> Int {
var runningTotal = 0
func incrementor ()-> Int {
runningTotal + = amount
return runningTotal
}
return incrementor
}
makeIncrementor return type is ()-> Int. This means that it returns a function, not a simple type value. The function does not accept parameters every time it is called and only returns a value of type Int. For the content of functions returning other functions, please see Function Types as Return Types.
The makeIncrementor function defines an integer variable runningTotal (initial is 0) to store the current total increase. This value is returned via incrementor.
makeIncrementor has a parameter of type Int, whose external name is forIncrement and internal name is amount, which represents the amount that runningTotal will increase each time incrementor is called.
The incrementor function is used to perform the actual increase operation. This function simply increases runningTotal by the amount and returns it.
If we look at this function alone, we will find that it looks unusual:
func incrementor ()-> Int {
runningTotal + = amount
return runningTotal
}
The incrementor function does not get any parameters, but the runningTotal and amount variables are accessed in the function body. This is because it is achieved by capturing the runningTotal and amount variables that already exist in the function body that contains it.
Since the amount variable is not modified, the incrementor actually captures and stores a copy of the variable, and the copy is stored with the incrementor.
However, because the value of runningTotal is modified each time the function is called, the incrementor captures the current runningTotal variable reference instead of just copying the initial value of the variable. Capturing a reference guarantees that it will not disappear when makeIncrementor ends, and also guarantees that runningTotal can continue to increase when the incrementor function is executed next time.
note:
Swift will decide whether to capture the reference or copy the value. You don't need to label amount or runningTotal to declare the usage in the embedded incrementor function. Swift also handles the memory management operation of the runningTotal variable, which will be cleared if it is no longer used by the incrementor function.
let incrementByTen = makeIncrementor (forIncrement: 10)
This example defines a constant called incrementByTen, which points to an incrementor function that adds 10 to each call. Call this function multiple times to get the following results:
incrementByTen ()
// The returned value is 10
incrementByTen ()
// The returned value is 20
incrementByTen ()
// The returned value is 30
If you create another incrementor, it will have a reference to its own independent runningTotal variable. In the following example, incrementBySevne captures a new runningTotal variable, which has nothing to do with the variable captured in incrementByTen:
let incrementBySeven = makeIncrementor (forIncrement: 7)
incrementBySeven ()
// The returned value is 7
incrementByTen ()
// The returned value is 40
If your closure is assigned to an attribute of a class instance, and the closure captures the instance by pointing to the instance or its members, you will create a strong reference ring between the closure and the instance. Swift uses a capture list to break this strong reference ring
Closures are reference types
In the above example, incrementBySeven and incrementByTen are constants, but the closures pointed to by these constants can still increase the value of their captured variables. This is because functions and closures are reference types.
Whether you assign a function / closure to a constant or a variable, you are actually setting the value of the constant / variable to the reference of the corresponding function / closure. In the above example, the reference to incrementByTen to the closure is a constant, not the closure content itself.
This also means that if you assign closures to two different constants / variables, both values will point to the same closure:
let alsoIncrementByTen = incrementByTen
alsoIncrementByTen ()
// The returned value is 50
[Learning in Swift] Swift Programming Journey-Closure (11)
label:
Original address: http://www.cnblogs.com/salam/p/5427743.html
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.