This post first appeared on my personal blog
What is a closure
- A function combined with its captured variable constant environment is called a closure
- Usually a function defined inside a function
- Generally it captures the local variable \ constant of the outer function
- Think of a closure as an instance object of a class
- There is heap space inside
- The captured local variable \ constant is a member of the object (storing properties)
- The functions that make up closures are the methods defined inside the class
Eg: We have a function sum
Func sum(_ v1: Int, _ v2: Int) -> Int {v1 + v2}Copy the code
If you define a function using a closure expression
var fn = {
(v1: Int, v2: Int) -> Int in
return// use fn(10, 20)Copy the code
Of course, you can
{
(v1: Int, v2: Int) -> Int in
return v1 + v2
}(10, 20)
Copy the code
To sum it up
{(parameter list) -> Return value typeinFunction body code}Copy the code
Short for closure expression
We define the following function exec, which takes three parameters, namely two ints and a function, and this function takes two ints and returns an Int result. The function’s purpose is to pass the first two parameters to the third parameter (that is, the function) for execution, and then print the result
// function We define the following functionexecIt takes two ints and a function, and the function takes two ints and returns an Int.execThe func () function passes the first two arguments to a third argument (function) for execution, and then prints the result funcexec(v1: Int, v2: Int, fn: (Int, Int) -> Int) {
print(fn(v1, v2))
}
Copy the code
If defined using closure expressions
// Closure expressionexec(v1: 10, v2: 20, fn: {
(v1: Int, v2: Int) -> Int in
return v1 + v2
})
Copy the code
Of course, we can omit a lot, as follows
// Omit the parameter type because Swift can infer the type itselfexec(v1: 10, v2: 20, fn: {
v1, v2 in return v1 + v2
})
// returnYou can also omitexec(v1: 10, v2: 20, fn: {
v1, v2 inV1 + v2}) // omit the argument list, use$0Represents the 0th parameter,The $1Represents the first parameterexec(v1: 10, v2: 20, fn: {
$0 + The $1}) // final ellipsisexec(v1: 10, v2: 20, fn: +)
Copy the code
Following the closure
- If you take a long closure expression as the last argument to a function, using trailing closures can enhance the readability of the function
- A trailing closure is a closure expression written outside (after) the function call parentheses
We have the following closure expression as the last argument to the function
func exec(v1: Int, v2: Int, fn: (Int, Int) -> Int) {
print(fn(v1, v2))
}
Copy the code
Using the trailing closure for
exec(v1: 10, v2: 20) {
$0 + The $1
}
Copy the code
- If the closure expression is the only argument to the function and the trailing closure syntax is used, there is no need to write parentheses after the function name
// The closure expression is the only argument to the function funcexec(fn: (Int, Int) -> Int) {
print(fn(1, 2))
}
Copy the code
You can use trailing closures as follows
// Use trailing closures in any of the following three waysexec(fn: { $0 + The $1 })
exec() { $0 + The $1 }
exec { $0 + The $1 }
Copy the code
Trailing closure in action
The system’s own sort
Suppose we have an array of ints and want to sort the opposite elements
func numberSort() {
var arr = [6, 8, 1, 10]
arr.sort()
print(arr) //[1, 6, 8, 10]
}
numberSort()
Copy the code
The printed result is
[1, 6, 8, 10]
Check out the official source for sort as
Func sort(by areInIncreasingOrder: (Element, Element) -> Bool)Copy the code
Custom sort
Let’s say we want to customize the sort
/ / / returntrueI1 precedes i2 // returnfalseFunc CMP (i1: Int, i2: Int) -> Boolreturn i1 > i2
}
Copy the code
When used
var nums = [6, 8, 1, 10]
nums.sort(by: cmp)
print(nums)
Copy the code
The printed result is
[10, 8, 6, 1)
Write with trailing closures
The code above
Can be written as
nums.sort(by: {
(i1: Int, i2: Int) -> Bool in
return i1 > i2
})
Copy the code
It can also be equivalent to the following
nums.sort(by: { i1, i2 in return i1 > i2 })
nums.sort(by: { i1, i2 in i1 > i2 })
nums.sort(by: { $0 > The $1 })
nums.sort(by: > )
nums.sort() { $0 > The $1 }
nums.sort { $0 > The $1 }
Copy the code
Ignore the parameters
In Swift, a lot of times, if we don’t do anything with the parameters, we can use underscores instead
For example, the following closure
func exec(fn: (Int, Int) -> Int) {
print(fn(1, 2))
}
print(exec{_, _in 100 }) // 100
Copy the code
The output
100
Automatic closure
function
Suppose we define a function that returns the first term if the first term is greater than 0. Otherwise return the second number
func getFirstPositive(_ v1: Int, _ v2: Int) -> Int? {
returnv1 > 0 ? V1: v2} getFirstPositive(10, 20) getFirstPositive(-2, 20) getFirstPositive(0, -4) // -4Copy the code
Now let’s say we pass it in this way
Func getNum() -> Int {// this is done every timelet a = 100
let b = 200
return a + b
}
func getFirstPositive2(_ v1: Int, _ v2: Int) -> Int? {
return v1 > 0 ? v1 : v2
}
getFirstPositive2(10, getNum())
Copy the code
Change the parameter of the function type
Since the first argument is already 10 greater than 0, the second argument, getNum(), does not need to be executed, wasting performance, so is there any way to execute getNum() only when the first argument is not satisfied? The answer is yes
Func getFirstPositive2(_ v1: Int, _ v2: () -> Int) -> Int? {// v1 > 0 does not call v2()returnv1 > 0 ? V1: v2()} getFirstPositive2(10, {// if the first argument is greater than 0, this is not executedlet a = 100
let b = 200
return a + b
})
Copy the code
To improve the
If I write it like this, I get an error
Func getFirstPositive2(_ v1: Int, _ v2: () -> Int) -> Int? {// v1 > 0 does not call v2()returnv1 > 0 ? V1: v2()} getFirstPositive2(10, 20type 'Int' to expected argument type '() -> Int'
Copy the code
Because () -> Int is required, Int is given
We could write it either way
Func getFirstPositive2(_ v1: Int, _ v2: () -> Int) -> Int? {// v1 > 0 does not call v2()return v1 > 0 ? v1 : v2()
}
getFirstPositive2(10) { 20}
getFirstPositive2(10, {20})
Copy the code
@autoclosure
The above can also be used with automatic closure technology
func getFirstPositive3(_ v1: Int, _ v2: @autoclosure () -> Int) -> Int? {
return v1 > 0 ? v1 : v2()
}
getFirstPositive3(-4, 20)
Copy the code
Points to note:
- To avoid conflicts with expectations, it is best to make it clear where @Autoclosure is used that this value will be deferred
- @autoClosure will automatically wrap 20 into a closure {20}
- @autoclosure supports only () -> T arguments n@autoclosure, not just the last one
- Null merge operator?? The @Autoclosure technique is used
- @autoclosure and no @Autoclosure constitute function overloading
Swift official source code
From beginner to proficient in Swift programming
More information, welcome to pay attention to the individual public number, not regularly share a variety of technical articles.