Swift Closures
Closures are self-contained functional code blocks that can be used in code or passed as parameters.
Closures in Swift are similar to blocks in C and Objective-C and to anonymous functions in some other programming languages.
Global functions and nested functions are actually special kinds of closures.
The forms of closures are:
| Global functions | Nested functions | Closure expressions |
| Have names but cannot capture any values. | Have names and can capture values from their enclosing function. | Unnamed closures, using lightweight syntax, can capture values based on the surrounding context. |
Closures in Swift have many optimizations:
- Infer parameter and return value types from context
- Implicit return from single-expression closures (that is, if the closure body has only one line of code, return can be omitted)
- Can use simplified parameter names, such as $0, $1 (starting from 0, representing the first, second parameters...)
- Provides trailing closure syntax
- An array of a known type
- A closure function. This closure function needs to take two values of the same type as the array elements and return a Boolean value to indicate whether the first argument should appear before the second argument when sorting is complete. If the first argument value appears before the second argument value, the sorting closure function needs to returntrue, otherwise it returnsfalse。
Syntax
The following defines the syntax of a closure that accepts parameters and returns a specified type:
{(parameters) -> return type in
statements
}
Example
import Cocoa
let studname = { print("Swift 闭包实例。") }
studname()
The output of the above program execution is:
Swift 闭包实例。
The following closure form accepts two parameters and returns a Boolean value:
{(Int, Int) -> Bool in
Statement1
Statement 2
---
Statement n
}
Example
import Cocoa
let divide = {(val1: Int, val2: Int) -> Int in
return val1 / val2
}
let result = divide(200, 20)
print (result)
The output of the above program execution is:
10
Closure Expressions
Closure expressions are a way to construct inline closures using concise syntax. Closure expressions provide some syntactic optimizations that make writing closures simple and clear.
The sorted Method
The Swift standard library provides a method namedsorted(by:)which sorts the values in an array of a known type according to the closure function you provide for sorting.
After sorting is complete, the sorted(by:) method returns a new array that is the same size as the original array, contains elements of the same type, and has its elements correctly sorted. The original array is not modified by the sorted(by:) method.
The sorted(by:) method needs two parameters to be passed in:
Example
import Cocoa
let names = ["AT", "AE", "D", "S", "BE"]
// 使用普通函数(或内嵌函数)提供排序功能,闭包函数类型需为(String, String) -> Bool。
func backwards(s1: String, s2: String) -> Bool {
return s1 > s2
}
var reversed = names.sorted(by: backwards)
print(reversed)
The output of the above program execution is:
["S", "D", "BE", "AT", "AE"]
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. For characters in a string, "greater than" means "appearing later alphabetically". This means that the letter "B" is greater than the letter "A", and the string "S" is greater than the string "D". It will sort in reverse alphabetical order, with "AT" appearing before "AE".
Shorthand Argument Names
Swift automatically provides shorthand argument names for inline closures. You can directly call the closure's parameters in order using $0, $1, $2.
Example
import Cocoa
let names = ["AT", "AE", "D", "S", "BE"]
var reversed = names.sorted( by: { $0 > $1 } )
print(reversed)
$0 and $1 represent the first and second String-type parameters in the closure.
The output of the above program execution is:
["S", "D", "BE", "AT", "AE"]
If you use shorthand argument names in a closure expression, you can omit their definition in the closure's parameter list, and the types of the shorthand argument names will be inferred from the function type. The in keyword can also be omitted.
Operator Functions
In fact, there is an even shorter way to write the closure expression in the above example.
Swift'sStringtype defines the string implementation for the greater-than operator (>) which, as a function, takes twoStringtype parameters and returnsBooltype value.sort(_:)matches the function type required by the method's second parameter.
import Cocoa let names = ["AT", "AE", "D", "S", "BE"] var reversed = names.sorted(by: >) print(reversed)
The output of the above program execution is:
["S", "D", "BE", "AT", "AE"]
Trailing Closures
A trailing closure is a closure expression written after the function's parentheses, and the function supports calling it as its last argument.
func someFunctionThatTakesAClosure(closure: () -> Void) {
// 函数体部分
}
// 以下是不使用尾随闭包进行函数调用
someFunctionThatTakesAClosure({
// 闭包主体部分
})
// 以下是使用尾随闭包进行函数调用
someFunctionThatTakesAClosure() {
// 闭包主体部分
}
Example
import Cocoa
let names = ["AT", "AE", "D", "S", "BE"]
//尾随闭包
var reversed = names.sorted() { $0 > $1 }
print(reversed)
The { $0 > $1 } after sort() is a trailing closure.
The output of the above program execution is:
["S", "D", "BE", "AT", "AE"]
Note: If the function only needs a closure expression as its single parameter, when using a trailing closure, you can even
()omit it.reversed = names.sorted { $0 > $1 }
Capturing Values
Closures can capture constants or variables from the context in which they are defined.
Even if the original scope where these constants and variables were defined no longer exists, the closure can still reference and modify these values within its closure body.
The simplest form of closure in Swift is a nested function, that is, a function defined within the function body of another function.
Nested functions can capture all the parameters of their outer function as well as the constants and variables defined there.
Take a look at this example:
func makeIncrementor(forIncrement amount: Int) -> () -> Int {
var runningTotal = 0
func incrementor() -> Int {
runningTotal += amount
return runningTotal
}
return incrementor
}
A function makeIncrementor, which has an Int parameter named amout, and it has an external parameter name forIncremet, meaning that when you call it, you must use this external name. The return value is a()-> Intfunction.
Inside the function body, a variable runningTotal and a function incrementor are declared.
The incrementor function does not take any parameters, but it accesses the runningTotal and amount variables inside its function body. This is achieved by capturing the runningTotal and amount variables that already exist in the enclosing function body.
Since the amount variable is not modified, incrementor actually captures and stores a copy of that variable, and that copy is stored together with incrementor.
Therefore, when we call this function, it will accumulate:
import Cocoa
func makeIncrementor(forIncrement amount: Int) -> () -> Int {
var runningTotal = 0
func incrementor() -> Int {
runningTotal += amount
return runningTotal
}
return incrementor
}
let incrementByTen = makeIncrementor(forIncrement: 10)
// 返回的值为10
print(incrementByTen())
// 返回的值为20
print(incrementByTen())
// 返回的值为30
print(incrementByTen())
The output of the above program execution is:
10 20 30
Closures Are Reference Types
In the above example, incrementByTen is a constant, but the closure to which these constants point can still increase the value of the variable it captured.
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 that constant/variable to a reference to the corresponding function/closure. In the above example, the reference to the closure that incrementByTen points to is a constant, not the closure content itself.
This also means that if you assign the closure to two different constants/variables, both values will point to the same closure:
import Cocoa
func makeIncrementor(forIncrement amount: Int) -> () -> Int {
var runningTotal = 0
func incrementor() -> Int {
runningTotal += amount
return runningTotal
}
return incrementor
}
let incrementByTen = makeIncrementor(forIncrement: 10)
// 返回的值为10
incrementByTen()
// 返回的值为20
incrementByTen()
// 返回的值为30
incrementByTen()
// 返回的值为40
incrementByTen()
let alsoIncrementByTen = incrementByTen
// 返回的值也为50
print(alsoIncrementByTen())
The output of the above program execution is:
50Other Extensions