Monday, May 13, 2019

Closure and function in swift

In swift, closure and function are same type as they are defined with the same format as below

      var closureargu : (Int, String)->String 

The above variable can be set either to a function by assigning it to a function name, or it can be set to a closure block.

A function is a particular form of closure, which includes the parameter name information, similar as closure's body implementation.

Even if a function has specified argument name, when the function is assigned to a closure variable, invoking the function by the closure variable does not need to specify the argument name as the closure variable only knows the type information without the argument name information. So once a function or closure body is assigned to a closure variable, it does not know whether the body implementation is from a function or closure.

Actually, when function or signaure used as a type, it cannot include argument label information, otherwise xcode will generate a compile error of "Function types cannot have argument labels" error. For example, the below code does not compile as (sumValue : Result, item : Element) -> Result) defines a function type with argument labels in it. 

func reduce<Result>(initial : Result, sum : (sumValue : Result, item : Element) -> Result) -> Result {
        var total = initial
        for it in self {
            total = sum(total, it)
        }
        return total
    }

To fix the compile error, the code should be changed as
func reduce<Result>(initial : Result, sum : (Result, Element) -> Result) -> Result {
        var total = initial
        for it in self {
            total = sum(total, it)
        }
        return total
    }
}


The below sample shows this behavior

var handlerWithParamAndValue : (Int, String) -> String  =  {pi, ps in
        return pi.description + " " + ps

}


func functionSample(a1 a: Int,  b1 b : String) -> String {
        return a.description + b.description
}
    
(Theoretically, the below code should not need @escaping keyword, as closure cannot be called after the function returns. However, without specifying @escaping, a compile error happens in xcode)

func someFunction(_ p1: Int, _ p2: String,  clo: @escaping (Int, String)->String) -> String {

       //assign function to variable
        var closureargu : (Int, String)->String = functionSample
        var re = closureargu(2, "def")
        
        //assign closure variable 
        closureargu = handlerWithParamAndValue
        re = closureargu(3, "mnq")
    
        //assign closure body
        closureargu = {p1, p2 in
           return p1.description + p2.description
        }
        re = closureargu(4, "me")
        
        //assign input closure parameter
        closureargu = clo
        re = clo(p1, p2)
        re = closureargu(p1, p2)
        
        //direct invoke function
        re = functioinSample(a1: 1, b1: "hihi")
        print(re)
        return re
}


//call the testing method
let r = someFunction( 1, "string", clo: {(a1 , a2) in

            let m = a1 + a1
            return a2.description + m.description          
        })


One unique feature to Closure is it allows specifying a capture list  before parameter list to change the self or other caller scope variables to weak or unowned, as shown below
  1. var asHTML: () -> String = {
  2. [unowned self] in
  3. if let text = self.text {
  4. return "<\(self.name)>\(text)</\(self.name)>"
  5. } else {
  6. return "<\(self.name) />"
  7. }
  8. }
Closure can update the value of the captured variables in outer scope, as long as the variable is mutable (defined by var, not by let).

No comments:

Post a Comment