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.