Painted levels: being fostered fostered fostered
Tags: “iOS” “Swift 5.1” “Closures” “Escape closures” “Trailing closures
Closures: Closures
Closures are separate blocks of function that can be passed and used in code. Closures in Swift are similar to blocks in C and Objective-C and lambdas(anonymous functions) in other programming languages. Closures can capture and store references to any constants and variables defined in the context. Global and nested functions are really special cases of closures. Closures take one of three forms:
- A global function is a closure that has a name but does not capture any value.
- A nested function is a closure that has a name and can capture values from its enclosing function.
- A Closure expression is an unnamed Closure that captures values from the surrounding context, written in lightweight syntax.
Swift’s closure expressions encourage brevity, so closures have:
- Infer the parameter and return value types from the context
- Implicit return of a single-expression closure
- Concise parameter names
- Trailing closure syntax
Closure expression
Nested functions can name and define independent functions as part of a larger function. Closure expressions optimize the construction of functions without having to fully declare and name them. Closure expressions are a way to write inline closures in a short, focused syntax. The optimization made by the Closure expression is illustrated using the sort (by 🙂 method.
Sorting method
Sorted (by 🙂 method that sorts arrays of known types based on the sort closure we provide. The sorted(by:) method returns a new array of the same type and size as the old one, sorted in the correct order. The sorted (by 🙂 method takes a closure that takes two arguments of the same type as the elements in the array and returns a Bool. True indicates that the first value appears before the second. If false is returned, the first value appears after the second value.
Let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"] // Define a descending sort function func backward(_ s1: String, _ s2: } let newArray = names.sorted(by: backward) print(newArray)//! < ["Ewa", "Daniella", "Chris", "Barry", "Alex"]Copy the code
Closure expression syntax
Closure expression syntax has the following general form:
{ (<#parameters#>) -> <#returnType#> in
<#statements#>
}
Copy the code
Arguments in closure expression syntax can be input and output arguments, but cannot have default values. You can use variadic parameters. Tuples can also be used as parameter types and return types. The above example uses closure expression syntax to write a sort closure inline:
// Sort array let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"] let newArray = names.sorted(by: {(s1:String,s2:String)->Bool in return s1 > s2 }) print(newArray)//! < ["Ewa", "Daniella", "Chris", "Barry", "Alex"]Copy the code
For inline closure expressions, arguments and return types are written inside curly braces, not outside. The beginning of the method body of a closure is introduced by the in keyword. This keyword indicates that the definition of the closure’s parameters and return types is complete and that the closure’s method body is about to begin.
Infer types from context
Because the sort closure (function type) is passed to the method as an argument, Swift can infer the type of its argument as well as the type of the value it returns. Also, the sort (by 🙂 method is called on an array of strings, so the sort closure is of type (String, String) – > Bool. Since all types can be inferred, the sorted closure expressions (String, String) and Bool return values can also be omitted, which means that the parentheses around the return value type and parameter name can be omitted:
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"] let newArray = names.sorted(by: Sorted {(s1, s2) -> Bool in return s1 > s2} print(newArray)//! < ["Ewa", "Daniella", "Chris", "Barry", "Alex"] print(names)//! < ["Chris", "Alex", "Ewa", "Barry", "Daniella"]Copy the code
When you pass a closure as an inline closure expression to a function or method, you can always infer the parameter types and return types. Therefore, when a closure is used as a function or method argument, it is never necessary to write the implicit return single-expression (s1>s2) of an inline closure closure in the most complete form. Closures can implicitly return the result of a single expression by omitting the return keyword from the closure declaration, so the above example could also be written as:
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
let newArray = names.sorted(by: {s1,s2 in s1 > s2})
Copy the code
The sorted (by 🙂 method is a function type argument that clearly indicates that the closure must return a Bool. Because the closure’s method body contains an expression s1> s2 that returns a Bool, there is no ambiguity, and the return keyword can be omitted.
Shorthand parameter name
A shorthand parameter name that references the value of the closure parameter with names $0, $1, $2, and so on. If these abbreviated parameter names are used in a closure expression, the closure’s parameter list can be omitted from the closure expression, and the number and type of the abbreviated parameter names will be inferred from the function type. The in keyword can also be omitted.
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
let newArray = names.sorted(by: {$0 > $1})
Copy the code
Closure expressions above operator methods actually have a shorter way to write them. Swift’s String defines its greater than the operator > implementation as a method that takes two String arguments and returns a value of type Bool. This exactly matches the method type required by the sorted (by 🙂 method. Therefore, you can simply pass in the operator >.
let newArray = names.sorted(by: >)
Copy the code
Following the closure
If you need to pass a closure expression as the function’s final argument, and the closure expression is long, you can write it as a trailing closure. A trailing closure is written after the parentheses of a function call, even if it is the function’s final argument. If a trailing closure is the final argument to a function and the corresponding argument label is defined, you cannot write the closure argument label as part of the function call when using the trailing closure syntax.
Func trailingClosures(parameter:String,block:(_:String)->Void) ->Void {block(parameter +" // Call func blockFunction(_ paramter:String)->Void {print(paramter)} TrailingClosures (parameter, "Not trailing closure 2", block, closures). {(parameter:String)->Void in print(parameter)}) trailingClosures. {(parameter)in print(parameter)})//! < Parameter types and return values are known based on function types. Therefore, it is possible to omit // trailing closure calls. Block function tags cannot appear in parentheses. {(parameter) in print(parameter)}Copy the code
Given the sorted (by 🙂 trailing closure
let newArray = names.sorted(){s1,s2 in s1 > s2}
let newArray = names.sorted(){$0>$1}
Copy the code
If the closure expression is the only argument to a function, we do not need to write a pair of parentheses () after the function name.
let newArray = names.sorted{s1,s2 in s1 > s2}
let newArray = names.sorted{$0>$1}
Copy the code
The value of the capture
A closure can capture constants and variables from the context around which it is defined. A closure can reference and modify the values of constants and variables in its method body, even if the original scope that defined the constants and variables does not exist. In Swift, the simplest form in which a closure can capture a value is a nested function. A nested function can capture any parameters of its enclosing function as well as constants and variables defined in the enclosing function.
// describe the function method of the closure to capture values. Func incrementer (forIncrese amount:Int) -> ()->Int {var total = 0; Total += amount return total}Copy the code
Representation analysis of closure capture values
let increaseByTen = createIncrementer(forIncrese: 10)//increaseByTen is an internal nested function (a kind of closure) that captures the createIncrementer parameter 'amount' and the 'total' variable defined in the method body. print(increaseByTen())//! < log:10 print(increaseByTen())//! < log:20 print(increaseByTen())//! < log:30 The 'increaseByTen' function captures the createIncrementer parameter 'amount' and the 'total' variable defined in the method body. It also holds internal copies of the external variable 'total' and the external method parameter 'amount'. So that 'increaseByTen' can increment the variable 'total' based on the value of 'amount' each time and return the result. let increaseBySix = createIncrementer(forIncrese: 6) print(increaseBySix())//! < log:6 print(increaseBySix())//! < log:12 print(increaseBySix())//! < log:18 Summary: 'increaseBySix' is a constant of type '()->Int' generated by calling 'createIncrementer'. During the build, the internal closure (nested function) 'increase' recapitles the external variable 'total' and the external method parameter 'amount', and returns this method assignment to 'increaseBySix', So much so that 'increaseBySix' and 'increaseByTen' have different increments. These are essentially constants of two different function types.Copy the code
In the example above, increaseByTen is actually an internal nested function (a type of closure) increase. The increaseByTen function captures the createIncrementer method’s amount argument and the total variable defined in the method body, and holds a copy of the value captured by the increaseByTen function inside it. That is, increaseByTen can increment the total variable based on the value of amount and return the result each time it is called. IncreaseBySix is another ()->Void constant generated by calling createIncrementer. During the generation, the inner closure (nested function) increase recapitches the external variable total and the external method parameter amount, and returns the method assignment to increaseBySix, so that the increaseBySix and increaseByTen have different increments. And make multiple calls separately. These are essentially constants of two different function types.
Note: If the value is not changed by the closure, it will not be changed after the closure is created. Swift can capture and store a copy of a value instead of a closure. Swift also participates in memory management to handle any variables that are no longer needed.
Closures are reference types
Functions and closures are reference types. Whenever you assign a function or closure to a constant or variable, you are actually setting that constant or variable to be a reference to the function or closure. Meaning that if you assign the same closure to two different constants or variables, then both constants or variables will reference the same closure.
let alsoIncreaseByTen = increaseByTen //! < reference-pass print(alsoIncreaseByTen())//! < log:40 let alsoIncreaseByTen1 = increaseByTen print(alsoIncreaseByTen1())//! < log:50Copy the code
As you can see from the above example, a function or closure is a reference type, and assignments between constants are actually passing references; whether alsoIncreaseByTen or alsoIncreaseByTen1 refers to the same function pointer. So the result of the call keeps increasing.
Escape a closure
A closure passed as a function parameter, but not called before the function returns, but after the function returns, is said to have escaped. When we declare a function that takes a closure as one of its arguments, we can indicate that closure escaping is allowed by writing @escaping before the type of the closure argument. That is: allows the closure to be called after the function ends. Escape closure is used when a function needs an asynchronous operation callback. One way to achieve closure escape is by storing the closure in a variable defined outside the function and calling it later. We will use this approach to describe the closure escape scenario.
Class escapeClosure: NSObject {// Declare an array of closure type. [(String)->Void] = [] Override init() {super.init()} // Mimic closure escape: After writing closureArray.append compiler detection to escape closure prompt add @escaping func EscapeClosures (title:String,handle:@escaping (String)->Void){closurearray.append (handle)} // Class method static func StartEscape () {let escapeObject = escapeClosure () escapeObject. EscapeClosures (title: "scenario 1) {(s1) in print (" scenario 1" + s1) / /! < 1 is called the scenario 1 escape closure.} escapeObject escapeClosures (title: "scenario 2") {(s1) in print (" scenario 2 "+ s1) / /! < scenario 2 escape closure 2 is called} / / use the iterator traverse along with the subscript and elements for (index, obj) in escapeObject. ClosureArray. Enumerated () { Obj (\ "escape closure (index + 1) is called the")}}} / / call escapeClosure startEscape () / /! < Scenario 1 escape closure 1 is called Scenario 2 escape closure 2 is calledCopy the code
Automatic closures: Autoclosures
An autoclosure is an automatically created closure that wraps expressions passed to functions as arguments. The expression is automatically created with no arguments, the return value omitted (depending on the expression’s return value) in keyword omitted, and the method body contains only the closure of the expression. When this function is called, the automatic closure returns the value of the expression. We can remove curly braces around automatic closures by using the keyword @autoclosure before the function type. But the key point is that using the @autoclosure keyword is limited to closures within arguments, and the closure type can have a return value, but absolutely no arguments. @autoclosure’ may only be used on parameters if you do something with the @autoclosure keyword that is not a parameter closure. Argument type of @autoClosure Parameter must be ‘()’ Argument type of @autoClosure Parameter must be ‘()’
Var nameArray = [" cloud "," yu "," zhang "," Liu "] // The closure argument is empty and omitted. The return value is String. Let removeClosure = {namearray.remove (at: 0)} print(" nameArray 'before calling' removeClosure ': \(namearray.count) After calling 'removeClosure', the string: \(removeClosure()) is removed and the array 'nameArray' becomes: \(namearray.count). You can see that automatic closures allow for delayed calls because the internal code does not run until the closure is called. ")Copy the code
In the example above, the number of nameArray arrays before removeClosure is called to remove the first element: 4. After calling removeClosure, remove the string: Zhao Yun and change the number of nameArray array to: 3. In summary, automatic closures allow for delayed invocation because the internal code does not run until the closure is called.
// Another form of deferred invocation, which defines a function that fits the function type of the automatic closure as an argument. The closure is defined and then called inside the function body to get the value. Static func unknownOperate(operation ()->String) -> Void {print(" operation())")//<! } // When passing the closure as an argument to the function, In the function of data elements to remove operation escapeClosure. UnknownOperate {() - > String in nameArray. Remove (at: 0)} / / more simplified form escapeClosure unknownOperate {nameArray. Remove (at: 0)}Copy the code
Use the keyword @autoclosure to mark closure arguments as automatic closures. When a function is called, arguments to a closure can be passed as if they were strings rather than closures.
Static func autoClosure(operation:) static func autoClosure(operation:) @AutoClosure ()->String){print(" operation() ")}} @autoClosure ()->String){print(" operation() ")} But also can let a person very be ever escapeClosure. AutoClosure (operation: nameArray. Remove (at: 0))Copy the code
Note: Overuse of Autoclosures makes our code difficult to understand. The context and function name should clearly indicate that the closure is being deferred. Automatic closures also allow escape. Both the @autoclosure and @escaping attributes are required.
Var autoclosureArray: [()->String] = [] @autoClosure @escaping ()->String) -> Void {autoclosureArray.append(handle)} // Perform automatic closure escape static func DoAutoclosureAndEscape (){var nameArray = [" I am an escapable automatic closure from array "] let escapeObj = escapeClosure() EscapeObj. AutoclosureAndEscape (handle: "I am the automatic closure of the escape") escapeObj. AutoclosureAndEscape (handle: nameArray. Remove (at: 0)) // Escape closure call, After the function returns the for (index, handle) in escapeObj. AutoclosureArray. Enumerated () {print (" \ [the handle ()) : \ (index) ")}} / / calls escapeClosure.doAutoclosureAndEscape()//! < Closure escape /* Console output: I am escapable closure: 0 I am escapable closure from array: 1 */Copy the code
Resources: Swift 5.1 official programming guide
Recommended articles:
Use SwiftUI to add animation to view with SwiftUI to write a simple page iOS control log switch iOS App can be removed a framework of two ways to customize WKWebView display content (A) Swift 5.1 (6) – Swift 5.1 function (5) – Control flow Xcode11 new project SceneDelegate iOS App startup optimization (2) — use “Time Profiler” tool to monitor the startup Time of App iOS App startup optimization (1) — understand the startup process of App qidance Weekly