Swift closures


Closures are self-contained functional code blocks that can be used in code or used to pass values ​​as parameters.

Closures in Swift are similar to blocks in C and Objective-C, as well as anonymous functions in some other programming languages.

Global functions and nested functions are actually special closures.

The form of closure is:

Global functionNested functionClosure expression
Has a name but cannot capture any value. With a name, it can also capture the value within the closed function. Unnamed closure, using lightweight syntax, can capture values ​​according to the context.

There are many optimizations for closures in Swift:

  1. Inferring parameter and return value types based on context

  2. From single-line expressions Implicit return in the closure (that is, the closure body has only one line of code, return can be omitted)

  3. You can use simplified parameter names, such as $0, $1 (starting from 0, indicating the i-th Parameters...)

  4. provides a trailing closure syntax

  5. Syntax

    The following defines a receive Parameters and returns the closure syntax of the specified type:

    {(parameters) -> return type in
       statements
    }

    Example

    import Cocoa
    
    let studname = { print("Swift 闭包实例。") }
    studname()

    The above program execution output is:

    Swift 闭包实例。

    The following closure form receives 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 execution output of the above program is:

    10

    Closure expression

    The closure expression is a A way to construct inline closures using concise syntax. Closure expressions provide some syntax optimizations that make writing closures simple and straightforward.


    sort function

    The Swift standard library provides a function called sort, which sorts the values ​​in an array of known types according to the closure function you provide for sorting. .

    After sorting is completed, the sort(_:) method will return a new array that is the same size as the original array, contains the same type of elements, and the elements have been correctly sorted. The original array will not be modified by the sort(_:) method. .

    The sort(_:) method requires two parameters:

    • Array of known type

    • Closure Function, this closure function needs to pass in two values ​​​​of the same type as the array elements, and returns a Boolean value to indicate whether the first parameter passed in is ranked before or after the second parameter after the sorting is completed. If the first parameter value appears before the second parameter value, the sorting closure function needs to return true, otherwise it returns false.

    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.sort(backwards)
    
    print(reversed)

    The above program execution output result 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 "occurring 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 be sorted in reverse alphabetical order, "AT" will be sorted before "AE".


    Parameter name abbreviation

    Swift automatically provides parameter name abbreviation function for inline functions. You can directly call the closure parameters sequentially through $0, $1, $2.

    Example

    import Cocoa
    
    let names = ["AT", "AE", "D", "S", "BE"]
    
    var reversed = names.sort( { 
    ["S", "D", "BE", "AT", "AE"]
     >  } ) print(reversed)

    $0 and $1 represent the first and second String type parameters in the closure.

    The output result of the execution of the above program is:

    import Cocoa
    
    let names = ["AT", "AE", "D", "S", "BE"]
    
    var reversed = names.sort(>)
    print(reversed)

    If you use the parameter name abbreviation in the closure expression, you can omit its definition in the closure parameter list and match the parameter name The abbreviated type is inferred from the function type. The in keyword can also be omitted.


    Operator function

    There is actually a shorter way to write the closure expression in the above example.

    Swift's String type defines the string implementation of the greater than sign (>), which accepts two String type parameters as a function And returns a value of type Bool. And this is consistent with the function type required by the second parameter of the sort(_:) method. Therefore, you can simply pass a greater than sign, and Swift can automatically infer that you want to use the greater than sign string function implementation:

    ["S", "D", "BE", "AT", "AE"]

    The output of the above program execution is:

    func someFunctionThatTakesAClosure(closure: () -> Void) {
        // 函数体部分
    }
    
    // 以下是不使用尾随闭包进行函数调用
    someFunctionThatTakesAClosure({
        // 闭包主体部分
    })
    
    // 以下是使用尾随闭包进行函数调用
    someFunctionThatTakesAClosure() {
      // 闭包主体部分
    }

    Trailing closure

    The trailing closure is a closure expression written after the function parentheses, and the function supports calling it as the last parameter.

    import Cocoa
    
    let names = ["AT", "AE", "D", "S", "BE"]
    
    //尾随闭包
    var reversed = names.sort() { 
    ["S", "D", "BE", "AT", "AE"]
     >  } print(reversed)

    Example

    reversed = names.sort { 
    func makeIncrementor(forIncrement amount: Int) -> () -> Int {
        var runningTotal = 0
        func incrementor() -> Int {
            runningTotal += amount
            return runningTotal
        }
        return incrementor
    }
     >  }

    { $0 > $1} after sort() is a trailing closure.

    The output result of the execution of the above program is:

    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())

    Note: If the function requires only one parameter, the closure expression, you can even omit () when you use a trailing closure.

    10
    20
    30

    Capturing values

    A closure can capture a constant or variable in the context in which it is defined.

    Even if the original domain in which these constants and variables are defined no longer exists, the closure can still reference and modify these values ​​​​in the closure function body.

    The simplest form of closure in Swift is a nested function, which is a function defined within the function body of other functions.

    Nested functions can capture all parameters of their external functions as well as defined constants and variables.

    Look at this example:

    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())

    A function makeIncrementor has an Int type parameter amout, and it has an external parameter name forIncremet, which means that when you call it, you must use this external name. The return value is a function of ()-> Int.

    In the function question, the variable runningTotal and a function incrementor are declared.

    The incrementor function does not obtain any parameters, but the runningTotal and amount variables are accessed in the function body. This is because it does so by capturing the runningTotal and amount variables that already exist within the body of the function 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 along with the incrementor.

    So when we call this function, it will accumulate:

    50

    The execution output of the above program is:

    rrreee

    The closure is a reference type

    In the above example, incrementByTen is a constant, but the closure pointed to by these constants can still increment the value of the variable it captures.

    This is because functions and closures are both 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 a reference to the corresponding function/closure. In the above example, the reference pointed by incrementByTen to the closure is a constant, not the closure content itself.

    This also means that if you assign a closure to two different constants/variables, both values ​​will point to the same closure:

    rrreee

    The output of the above program execution is:

    rrreee