1. Introduction
Functions are code segments with specific functions, and functions will be used when called with a specific name. Swift provides a very flexible way to create and call functions. In fact, in Swift, every function is a type, which is determined by the parameters and the return value. One of the major differences between Swift and Objective-C is that functions in Swift can be nested.
The closures in Swift are code blocks with certain functions, which is very similar to the block syntax in Objective-C. The closure syntax style in Swift is very concise, and its functions are similar to those of functions.
2. Create and call functions
Functions are defined by function names, parameters and return values. Parameters and return values determine the type of a function. When calling a function, use the function name to call. The example is as follows:
//Paste a name and print it backfunc printName(name:String) -> String { print(name) return name } //Make a function callprintName("HS")
You can also create functions without parameters:
func onePuseTwo()->Int { return 1+2 } onePuseTwo() You can also create functions without return values: func sayHello(){ print("Hello") } sayHello()
The function types introduced above are common. For functions with multiple return values, it is very difficult to deal with in Objective-C. Developers usually use dictionaries, arrays and other collection methods or simply use block callbacks. In Swift, tuples can be used as the return value of the function. The example is as follows:
func tuples()->(Int,String){ return (1,"1") } tuples()
It can also be a function that returns an Optional value, which supports returning nil. The example is as follows:
func func1(param:Int)->Int? { guard(param>0)else{ return nil } return param } func1(0) func1(1)
Before the function's parameter name, the developer can also add a parameter name as an external parameter name. The example is as follows:
func func1(count param:Int ,count2 param2:Int)->Int? { //Always use param internally guard(param>0)else{ return nil } return param } //Use count for external callsfunc1(count: 0,count2: 0) func1(count: 1,count2: 1)
In fact, the parameter list in the Swift function has such a feature. In addition to the first parameter, the subsequent parameters will be added by default with an external name with the same internal name. If the developer does not want to use this external name, use the _ symbol setting, the example is as follows:
func func2(param:Int,param2:Int,param3:Int) { } //There is an external namefunc2(0, param2: 0, param3: 0) func func3(param:Int,_ param2:Int,_ param3:Int) { } //No external namefunc3(0, 0, 0)
Swift also supports developers to create a default value for the parameters of a function. If a parameter of a function has a default value, the developer can omit this parameter when calling. The example is as follows:
func func4(param:Int=1,param2:Int=2,param3:Int) { print(param,param2,param3) } func4(3,param3:3)
There is another situation that is also very well handled in Objective-C. For functions with varying number of parameters, as mentioned in the previous chapter, Objective-C generally uses list pointers to complete it. It is very simple to write such functions in Swift. The example is as follows:
func func5(param:Int...) { for index in param { print(index) } } func5(1,2,3,4)
Parameters in Swift are constants by default. In functions, it is worth noting that external incoming parameters cannot be modified. If there is a requirement, the parameters need to be declared as inout type. The example is as follows:
func func6(inout param:Int) { param = 10 } var count = 1 //In fact, the parameter address is passed infunc6(&count) print(count)
3. Function type
Functions are a special data type, and each function belongs to a data type. The examples are as follows:
func func7(a:Int,_ b:Int)->Int{ return a+b } var addFunc:(Int,Int)->Int = func7 addFunc(1,2)
Functions can also be passed into another function as parameters, which is very similar to the block syntax in Objective-C. The example is as follows:
func func7(a:Int,_ b:Int)->Int{ return a+b } var addFunc:(Int,Int)->Int = func7 addFunc(1,2) func func8(param:Int,param2:Int,param3:(Int,Int)->Int) -> Int { return param3(param,param2) } //Passing in functionfunc8(1, param2: 2, param3: addFunc) //Closing methodfunc8(2, param2: 2, param3:{ (a:Int,b:Int) -> Int in return a*b })
A human function can also be used as a return value of another function. Examples are as follows:
func func9()->(Int)->Int{ //Swift supports nested functions func tmp(a:Int)->Int{ return a*a } return tmp } var myFunc = func9() myFunc(3)
4. Look at closures from a system function
The Swift standard function library provides a sort sort function. For arrays with already element types, calling the sort function will reorder and return the new sorted array. This sort function can receive a closure with the return value of Bool to determine whether the first element is ahead of the second element. The code example is as follows:
var array = [3,21,5,2,64] func func1(param1:Int,param2:Int) -> Bool { return param1>param2 } //By passing in the function//array = [64,21,5,3,2] array = (func1) //Closed//array = [2,3,5,21,64] array = ({(param:Int,param2:Int)->Bool in return param<param2 })
A very obvious feature of Swift language is its conciseness. You can infer the types through context. Generally, development can omit the writing of types. This is also an idea in Swift language design. Since closures are passed into the function as parameters of the function, because the type of function parameters is determined, the type of the closure can be inferred by the compiler, and the developer can also omit the parameter type and return value of the closure. The above code can be abbreviated as follows:
//Omit both the parameter type and return value of the closurearray = ({(p1,p2) in return p1>p2})
In fact, if the function body in the closure has only one line of code, the return keyword can be omitted, and the value of this line of code will be implicitly returned, as follows:
array = ({(p1,p2) in p1>p2})
Is it a little shocked to see the expression above? The closure expression can be abbreviated like this! However, you are still underestimating the Swift development team, and the subsequent grammar rules will let you understand what the ultimate simplicity is. You can see that the above code implementation still has 3 parts: parameters and return values, closure keywords, and function bodies. Parameters and return values are parameter lists, p1 and p2. Although parameter types and return value types are omitted, the modules in this part are still there. The closure keyword is in, which is used to indicate that the function body that will be closure below, p1>p2 is the function body, but the return keyword is omitted here. Since the compiler of the parameter type and return value type can be inferred by itself in the closure, the number of parameters editor can also infer by itself. Therefore, the parameter list is actually redundant. Some parameter names will be automatically generated in the closure, which correspond to the actual number of parameters. For example, the closure in the above sort function has two parameters, and the system will automatically generate two parameter names, $0 and $1. Developers can use it directly, because the parameter list will be omitted, and then there is no longer a closure keyword in to separate the parameter list and the function body. At this time, the writing of the closure actually becomes the following:
array = ({$0<$1})
You read that right, add curly braces on the left and right, with a total of 7 characters, and complete a sorting algorithm. Apart from Swift, I don't know if there is a second language that can do it. Putting aside the closure, there is also a syntax in Swift that can define operator methods of types. For example, String types can be compared with =, <,>. In fact, these operator methods are implemented in the String class. In a sense, an operator is similar to a function. So, the methods that need to be passed in the sort function actually only require an operator for some types. The example is as follows:
array = (>)
This time you can be really shocked. Only one character is needed to complete the new sorting algorithm, one character is absolutely necessary.
5. More features of closures in Swift
Closures in Swift also have an interesting feature. First, the closure is passed into another function as a parameter. Therefore, the conventional way of writing is to write the curly brackets of the closure in the parentheses of the function's parameter list. If there is a lot of code in the closure, it will not be very clear in the code structure. In order to solve this problem, Swift stipulates that if this closure parameter is the last parameter of the function, the developer can pull it out the brackets and implement the closure code at the end of the function. The example is as follows:
//The end of the closurefunc func2(param1:Int,param2:()->Void)->Void{ param2() print("Func2 function was called") } func2(0){ print("Content in closure") }
If there is only one parameter in a function and this parameter is a closure, then the developer can also omit the parentheses of the function's parameter list using the closure ending. The example is as follows:
func func3(param:()->Void)->Void{ param() print("Func3 function was called") } func3{ print("Content in closure") }
There is also a concept of closure escape in Swift. This is easy to understand. When a closure is passed into a function as a parameter, if the closure is only used in the function, the developer can declare the closure as non-escaping, that is, tell the system that the declaration cycle of the closure will also end after the function is finished. The advantage of this is that it can improve code performance. Use the @noescape keyword to declare the closure non-escaping type. The example is as follows:
func func3(@noescape param:()->Void)->Void{ param() print("Func3 function was called") } func3{ print("Content in closure") }
Escaped closures are often used for asynchronous operations. For example, this closure is to process a network request asynchronously. Only when the request is completed, the closure declaration cycle ends. Non-escaping closures also have an interesting feature. If the keyword self is needed to be used internally, self can be omitted.
Closures can also be automatically generated. This kind of closure is called automatic closure. Automatic closures can automatically encapsulate expressions into closures. Developers do not need to write the bracket format of the closure. The automatic closure does not receive parameters, and the return value is the value of the expression in it. Examples are as follows:
//Automatic closure demonstrationvar list = [1,2,3,4,5,6] //Create an explicit closurelet closures = { () (7) } // Will print [1,2,3,4,5,6]print(list) //Execute closureclosures() // will print [2,3,4,5,6,7]print(list) func func4(closure:()->Void) -> Void { //Execute explicit closure closures() } func func5(@autoclosure auto:()->Void) -> Void { //Execute automatic closure auto() } //Explanatory closure requires bracesfunc4(closures) // will print [3,4,5,6,7,7]print(list) //Create the expression automatically generates closurefunc5((8)) // Will print [3,4,5,6,7,7,8]print(list)
Automatic closures are non-escaping by default. If you want to use escaped closures, you need to declare them manually, as follows:
func func5(@autoclosure(escaping) auto:()->Void) -> Void { //Execute automatic closure auto() }