This is the 8th day of my participation in the More text Challenge. For details, see more text Challenge

closure

  • Closures are independent blocks of functional code that can be passed and used in code
  • Closures andblockThe contrast of
    • Closures in SWIFT are similar to blocks in OC
    • A closure in SWIFT is a special function in OCblockIs an anonymous function
    • Closures andblockAre often used for callbacks
    • Block expression
      Type: Return value type (^block name)(parameter to block) Return value (^block name)(parameter list) = ^(parameter list){// Execute the code
      };
      
      
      NSString* (^blockName)(NSString *str) = ^(NSString *str){
          return str;
      };
      
      blockName(@"block")
      Copy the code
    • Closure expression
      Type :(parameter)->(return value type) closure name = {(parameter list)->returnThe return valuein
          // Execute the code
      }
      let closure = { (str:String) - >String in
          return str
      }
      
      closure("closure")
      Copy the code

The closure type

  • No parameter No return value
    let closure = {()->Void in
         print("closure")
    }
    
    closure()
    Copy the code
  • Yes Parameter No return value
    let closure = {(str:String) -> Void in
        print(str)
    }
    
    closure("closure")
    Copy the code
  • No parameter has a return value
    let closure = {() -> String in
        return "closure"
    }
    
    closure()
    Copy the code
  • There are parameters and return values
    let closure = { (str:String) - >String in
        return str
    }
    
    closure("closure")
    Copy the code

Closure expression

The following example shows the definition and syntax optimization of the sorted(by:) method by using several iterations. Each iteration describes the same functionality in a more concise way

  • Function processing

    The sorted(by:) method accepts a closure that needs to pass in two values of the same type as the array elements and return a Boolean value to sort. The sort closure type must be :(Int, Int) -> Bool

    let numbers = [1.9.2.8.3.7.4.6];
    let numArr = numbers.sorted(by: callback)
    func callback(_ numA:Int, _ numB:Int) -> Bool {
        return numA < numB
    }
    print(numArr)    
    
    log:
    [1.2.3.4.6.7.8.9]
    Copy the code
  • Handled by closure expressions

    The inlined closure argument and return value type declarations are the same as the callback(_:_:) function type declarations

    let numbers = [1.9.2.8.3.7.4.6];
    let numArr = numbers.sorted { (numA:Int, numB:Int) -> Bool in
        return numA < numB
    }
    print (numArr) 
    
    log:
    [1.2.3.4.6.7.8.9]
    Copy the code
  • Infer types from context

    The sorted(by:) method is called with an array of ints, so its arguments must be functions of type (Int, Int) -> Bool. Since all types can be inferred correctly, the return arrow (->) and the parentheses surrounding the arguments can also be omitted

    let numbers = [1.9.2.8.3.7.4.6];
    let numArr = numbers.sorted { numA,numB in 
        return numA < numB 
    }
    print(numArr)
    
    log
    [1.2.3.4.6.7.8.9]
    Copy the code
  • Single-expression closures return implicitly

    The sorted(by:) method argument type specifies that the closure must return a value of type Bool, because the closure body contains only a single expression (s1 > s2) that returns a value of type Bool, so there is no ambiguity here and the return keyword can be omitted

    let numbers = [1.9.2.8.3.7.4.6];
    let numArr = numbers.sorted { numA,numB in numA < numB }
    print(numArr)    
    
    log:
    [1.2.3.4.6.7.8.9]
    Copy the code
  • Parameter name abbreviation

    You can call the arguments of the closure directly with $0,$1,$2, and so on. If you use an argument name abbreviation in a closure expression, you can omit the argument list from the closure definition, and the type of the corresponding argument name abbreviation is inferred from the function type. The in keyword can also be omitted because the closure expression consists entirely of the body of the closure function

    let numbers = [1.9.2.8.3.7.4.6];
    let numArr = numbers.sorted {$0The < $1}
    print(numArr)
    
    log:
    [1.2.3.4.6.7.8.9]
    Copy the code
  • Operator method

    Swift’s Int type defines an implementation of the greater-than sign (>) as a function that takes two ints and returns a Bool. This, in turn, matches the function type required for the argument to the sorted(by:) method. Therefore, you can simply pass a greater-than sign

    let numbers = [1.9.2.8.3.7.4.6];
    let numArr = numbers.sorted(by: <)
    print(numArr)
    
    log:
    [1.2.3.4.6.7.8.9]
    Copy the code

Following the closure

An anonymous closure is passed as an argument to an ordinary function

  • If the closure is the last argument to the function, then the closure is called after parentheses ()

    func sayHi(str1:String.str2:String.closure: (String.String) - >String) - >String{
        return closure(str1,str2);
    }
    
    let message = sayHi(str1: "hello", str2: "world") { (str1:String.str2:String) - > (String) in
        return str1 + "" + str2
    }
    
    print(message)
    
    log:
    hello world
    Copy the code
  • If the function has only one argument and is a closure, the parenthesis () can be called without being written

    func sayHi(closure:(String)->())-> Void {
    
        print("There's only one parameter.")
        closure("hello world")
    }
    
    sayHi { (str:String)  in
        print(str)
    }
    
    log: Only takes one argument hello worldCopy the code

Value capture

Closures can capture constants or variables in the context in which they are defined. Even if the original scope in which these constants and variables were defined no longer exists, the closure can still refer to and modify these values within the closure function body.

func add(num:Int) -> ()->Int {
    var value = 0
    func result() -> Int{
        value += num
        return value
    }
    /* There is a function called add, which contains a nested function called result. The nested function result() captures two values from the context. After the values are captured by value and num, add returns result as a closure each time result is called. It increments value */ by num
    return result
}
Copy the code
  • A nested function captures all the parameters of its external function, as well as defined constants and variables, and ensures that the values captured are still present the next time the function is executed

    func add(num:Int) -> ()->Int {
        var value = 0
        func result() -> Int{
            value += num
            return value
        }
        return result
    }
    
    let result = add(num: 10)
    print(result())  / / 10
    print(result())  / / 20
    print(result())  / / 30
    Copy the code
  • Variables in the same method are bound to their own variables

    func add(num:Int) -> ()->Int {
        var value = 0
        func result() -> Int{
            value += num
            return value
        }
        return result
    }
    
    
    // If you create another result1, it will have its own reference to a new, independent value variable
    let result1 = add(num: 10)
    print(result1())  / / 10
    
    // Calling result again will add its own value variable, which has no relation to the variable captured in result1
    print(result())  / / 40
    Copy the code

Closures are reference types

  • Both functions and closures are reference types

    Assigning a function or closure to a constant or variable is really setting the value of the constant or variable to a reference to the corresponding function or closure

    // Both constants or variables refer to the same closure
    let method = result
    Copy the code

Escape a closure

  • A closure passed to a function that is not called until after the function has finished executing is called an escape closure. In general terms, you do not use closures within the current method, but outside the method
  • When you define a function as an escaping closure, you simply need to preceded the argument name with @escaping to indicate that the closure will allow “escaping” out of the function
  • Mark a closure as@escapingMeans you must explicitly reference it in the closureself
    var result: ()->Void = {}
    var str = ""
    func showA(closure: @escaping () -> Void) {
        result = closure
    }
    func showB(closure: () -> Void) {
        closure()
    }
    func doSomething() {
        showA {str = "I am the escape closure."}
        showB {str = "I'm a normal closure."}
    }
    
    doSomething()
    print(str)    // I'm a normal closure
    result()
    print(str)    // I am the escaping closure
    Copy the code

    The escaping closure is executed after the function has executed, so the code ends with “I am the escaping closure.”


Automatic closure

  • Automatic closure: Automatically creates a closure to wrap an expression. This closure takes no arguments and, when called, returns the value of the expression wrapped in the closure

  • Automatic closures allow you to delay evaluation because the code snippet will not be executed until you call the closure

  • This convenience syntax allows you to omit the closure curly braces and replace the explicit closure with a plain expression

    var arr = ["a"."b"."c"]
    print(arr.count)  / / 3
    let closure = {
        arr.remove(at: 0)
    }
    
    print(arr.count)  / / 3
    print(closure())  //a
    print(arr.count)  / / 2
    Copy the code
  • You can also delay evaluation when passing closures as arguments to a function

    The function takes an argument of an explicit closure type

    func delete(closure: ()->String){
        print(closure())
    }
    
    var arr = ["a"."b"."c"]
    delete(closure:{arr.remove(at: 0)})    
    
    log:
    a
    Copy the code

    Receive an autoclosure by marking the argument @autoclosure. This function accepts a String argument instead of a closure

    func delete(closure: @autoclosure ()-> String){
        print(closure())
    }
    
    var arr = ["a"."b"."c"]
    delete(closure:arr.remove(at: 0))  
    
    log:
    a
    Copy the code