[Swift] Closure

Let's understand closures

Closures are a reusable chunk of processing. Closures are easier to define than functions, such as not requiring a name or omitting a type by type inference.

Definition method

The types of arguments and return values ​​are similar to functions.

{(Argument name:Mold)->戻り値のMold in
Statement to be executed
Return statement if necessary
}

The following example defines a closure that takes one Int argument, doubles it, and returns it.

let double={(x:Int)->Int in
    return x*2
}
double(2)//4

Closures can also be used as variable or constant types, or as function argument types. (Details will be explained later.)

Type inference

The closure argument and return types may be omitted by inferring from the type to which the closure is assigned.

//When not omitting the type
let double_1={(x:Int)->Int in
    return x*2
}
//When omitting the type
let double_2={ x in
    return x*2
}
double_1(2)//4
double_2(2)//4

Here, closures double_1 and double_2 are declared in let, so closures cannot be changed. However, if you declare it using var (variable) as follows, even if you change the closure, you will not get an error unless you change the type. The following double_1 types are (Int)-> Int types

//Declare with var
var double_1={(x:Int)->Int in
    return x*2
}
//Declare with let
let double_2={(x)in
    return x*2
}
double_1={x in
    return 3
}
double_2={x in
    return 3
}//error
    
double_1(2)//3
print(type(of: double_1))//(Int)->Int

Closure argument features

You cannot use the external argument name (when calling from the outside, make it different from the argument in the closure) and the default argument (assigning a value to the argument when defining the closure).

Short argument name

As mentioned earlier, the type can be omitted if the closure type can be inferred. You can omit the argument name too !! However, readability will be reduced, so use it properly.

//No abbreviated argument name
let isEqual_1:(Int,Int)->Bool={(x,y) in
    return x==y
}
//With abbreviated argument name
let isEqual_2:(Int,Int)->Bool={
    return $0==$1
}
isEqual_1(2,2)//True
isEqual_2(2,2)//True

Finally closure as an argument

When using a closure as an argument to a function or another closure, there are attributes and trailing closures. Attributes are information that adds the nature of closures, and trailing closures are a specification for improving the readability of functions that take closures as arguments.

attribute

There are two types of attributes. It has an escaping attribute and an autoclosure attribute. This time, I will explain only the escaping attribute.

escaping attribute

The escaping attribute is an attribute that indicates that the closure passed as a function argument can be used outside the scope of the function. Also, add @ before the attribute.

pattern 1
var que=[()->Int]()
func number(operation: @escaping ()->Int){
     que.append(operation)
}
number(operation: {() in return 1})
number(operation: {() in return 2})

que[0]()//1
que[1]()//2


Pattern 2(Short argument name)
var que=[()->Int]()
func number(operation: @escaping ()->Int){
     que.append(operation)
}
number(operation: { return 1})
number(operation: { return 2})

que[0]()//1
que[1]()//2


Pattern 3
var que=[()->Int]()
func number(operation: @escaping ()->Int){
     que.append(operation)
}
number{ return 1}
number{ return 2}

que[0]()//1
que[1]()//2


Patterns 1, 2 and 3 have the same meaning. The number function is a function that adds a closure of type ()-> Int in an array of que. Executing number {return 1} adds a closure of {() in return 1} to que [0]. The next number {return 2} also adds {() in return 2} to que [1]. Also, if there is no escaping in the above code, an error will occur.

Trailing closure

Pattern 3 is exactly the trailing closure itself. If the function argument ends with a closure, you can omit () when calling the function. The bottom two below are patterns that use trailing closures.

func execute (param:Int,operation:(String)->Void){
    operation("The parameters are\(param)is.")
}
//When not using trailing closure
execute(param:1,operation: { string in print(string)})//The parameter is 1.

//When using a trailing closure
execute(param:2){ string in print(string) }//Parameter is 2
execute(param:3){  return print($0) }//Parameter is 3

That concludes the explanation of closures.

Recommended Posts

[Swift] Closure
HandleEvents closure not called in Swift Combine
[Swift] A note about function and closure
Swift UI 100 knocks
[Swift] Type type ~ Enumeration type ~
[Swift] About generics
[Swift] Dissect MessageKit
[Swift] Capture list
First Swift Lint
[Swift] Variadic arguments
[Swift] First Firebase
[Swift] Random color
[Swift] Display Hello Swift!
[Swift5] Code-only UITabBarController
Swift extension collection
[Swift] Type type ~ Structure ~
[Swift] Error handling
Swift Network.framework Study 20191203