Definition of a function

// No parameter function
func pi(a) -> Double {
    return 3.14
}
// function with parameters
func sum(v1: Int.v2: Int) -> Int { // The default parameter is let, and it can only be let
    return v1 + v2
}
// Function call
sum(v1: 10, v2: 20)

// No return value
func sayHello(a) -> Void {
    print("Hello")}func sayHello2(a)- > () {print("Hello")}func sayHello3(a) {
    print("Hello")}Copy the code


Implicit return

If the entire function body is a single expression, the function implicitly (automatically) returns that expression

// return implicitly
func sum2(v1: Int.v2: Int) -> Int { If a function has only one statement, it does not need to write return. If there are more than one statement, it must use the return keyword
     v1 + v2
}
// Function call
sum2(v1: 10, v2: 20)
Copy the code


Return tuple: Implements multiple return values

// Implement multiple return values through the meta-ancestor. Collate multiple return values into a meta-ancestor data structure for a single return
func calculate(v1: Int.v2: Int) -> (sum: Int, difference: Int, average: Int) {
    let sum = v1 + v2
    return (sum, v1 - v2, sum >> 1)}let result = calculate(v1: 20, v2: 10)
result.sum
result.difference
result.average
Copy the code


Comments on the function document

///
///
// add two integers together
///
/// -parameter v1: the first integer
/// -parameter v2: the second integer
/// - Returns: the sum of two integers
///
/// - Note: you can pass in two integers.
func sum(_ v1: Int._ v2: Int) -> Int {
    v1 + v2
}

Copy the code

The comments for the function documentation need to be filled out exactly as in the template above. Apple officials strongly recommend that we document the functions in detail to improve the readability of the code. The comment produces the following effect, invoked by the Option key + clicking on the function name.

Please refer to the interface design specification provided by Apple for more detailed comments on the function documentation


Parameters of the label

// In the function definition, it is easy to understand the nature or function of the passed parameter through the semantics of the parameter time
func goToWork(at time: String) {
    print("This time is \(time)")}// When the function is called, the actual argument is replaced by time,
goToWork(at: "In its")
Copy the code

In the above example, the parameter of the goToWork function has two tags: AT and time. Time, as a parameter, is used to pass the parameter in the internal implementation of the function body, while AT is used during the function call. As you can see from the above example, the function definition and call through the parameter label are very consistent with the spoken language. Using this feature provided by Apple and referring to our normal language habits to set the parameter label reasonably can improve the readability of the code. This also conforms to Apple’s API design guidelines.

func sum(_ v1: Int._ v2: Int) -> Int {
    v1 + v2
}
sum(10.20)
Copy the code

Using _ to omit argument labels allows function calls to be made without argument names, simplifying code. However, the use of this point should be combined with the actual situation, do not affect the readability of the code in order to simplify the code, thus bringing inconvenience to the later maintenance.


Default Parameter Value

First look at an example of a function with default values

func check(name: String = "nobody".age: Int.job: String = "none") {
    print("name=\(name), age=\(age), job=\(job)")
}
check(name: "Jack", age: 20, job: "Docter")
check(name: "Rose", age: 18)
check(age: 10, job: "Batman")
check(age: 15)
Copy the code

In this example, we can see that the age parameter does not have a default value. We can draw the conclusion that the age parameter must be passed, and the other parameters with default values can be passed or not, and there is no special order requirement.

You may not understand what I mean by this order requirement. If you’ve worked with C++, you know that C++ functions can also set default values for parameters, but they must be set from right to left without spacing. For example, here’s the test function

void test1(int a, int b, int c = 10.int d = 20){}// In the parameter list, set the default values for 'd' and 'c' from right to left respectively, which can be compiled at ✔️

void test2(int a = 10.int b, int c, int d = 20){}// Set the default values for 'd' and 'a' respectively from right to left, and skip 'c' and 'b' in the middle. Cannot compile through ✖️
Copy the code

Unlike swift, C++ does not have a parameter label, so C++ can only assign arguments in the order they are passed in, from left to right in the parameter list. So test1(50, 60) is easy to understand, plus the arguments with default values, is equivalent to test1(50, 60, 10, 20).

But test2(50, 60) is unintelligible to the computer because, according to c ++ parsing rules, 50 and 60 are assigned to parameters A and B, respectively. In fact, we want to assign parameters B and C, so test2 cannot be used in practice due to ambiguity. A lot of it is about eliminating ambiguities in code, because computers are stupid.

Swift can bind parameters and parameters according to the parameter labels, because the function calls with parameter labels. Therefore, we can set the default values of parameters for the Swift function regardless of the order. However, if we add _ to all the arguments in the function, the effect is equivalent to the C++ function, as follows

void testCpp(int a, int b, int c , int d ){}Copy the code

Is equivalent to

func testSwift(_ a: Int._ b: Int._ c: Int._ d: Int){}Copy the code

You can see that, even though we givetestSwiftSome of the parameters are set to silent parameters, but because of the order in which they are set, you actually have to pass values to all the parameters to make a successful call, meaning that the default parameters do not work as they should. If the referenceC++By setting the default values from right to left, you can make a function call by passing only non-default parameters, as seen abovetest


Variadic Parameter

func sum(_ numbers: Int...). -> Int {
    var total = 0
    for number in numbers {
        total + = number
    }
    return total
}
sum(1.3.4.5.50.90)
sum(5.88.2)
Copy the code
  • A functionThere can be at most oneVariable parameter
  • Parameters immediately following a variable parameter cannot omit the parameter labelThe purpose of this request is easy to understand, is to use the argument after the variable argumentParameters of the labelTo determine where the mutable argument ends.
// The string argument cannot omit labels
func test(_ numbers: Int. .string: String._other: String) {
    
}
test(10.20.40, string: "jack", _other: "rose")
Copy the code


Swift’s own print function

Swift’s print function is an example of variable arguments

/// - Parameters:
/// - items: Zero or more items to print.
/// - separator: A string to print between each item. The default is a single
/// space (`" "`).
/// - terminator: The string to print after all items have been printed. The
/// default is a newline (`"\n"`).
public func print_copy(_ items: Any..separator: String = "".terminator: String = "\n"){
    // System implementation
    }
Copy the code


In-out Parameter

  • You can useinoutDefine an input/output parameter:You can modify the values of external arguments inside a function
  • A variable parameter cannot be marked asinout
    1. inoutThe argument is essentially address passing (reference passing)
    2. inoutParameters cannot have default values
    3. inoutArguments can only be passed in values that can be assigned more than once
func swapValue(_ v1: inout Int._ v2: inout Int) {
    let tmp = v1
    v1 = v2
    v2 = tmp
}
var num1 = 10
var num2 = 20
swapValue(&num1, &num2)

func swapValue2(_ v1: inout Int._ v2: inout Int) {
// Use tuples to do this
    (v1, v2) = (v2, v1)
}
Copy the code

Now through assembly means, to study the implementation principle of Inout. In C, we use ampersand to access the address of a variable, but in Swift, this function is blocked. We can only use ampersand when passing inout parameters, otherwise the compiler will report an error.

First we need to create a new command line project

Prepare the following test code

var number = 10
func test(_ num: inout Int) {
    num = 20
}
func test2(_ num: Int) {
}

test(&number)
test2(number)
Copy the code

Add a breakpoint and go to assembly This is shown in the picturetest()andtest1()A compilation of these two functions, by passing the instructions used for the arguments, we get the result

  • leaq:testThe function uses this instruction to pass addresses, i.etestThe function takes a memory address as an argument
  • movq:test2The function uses the copy instruction to pass the value of an argument into the function

According to the above discovery, it is shown that the inout parameter is actually passed the address of the external variable. The default value doesn’t mean anything, and Swift doesn’t allow us to get the memory address, so there’s really no way to set the memory address to default.


Function Overload

  • The rules
    1. Same function name
    2. Parameter number different | | parameter types different | | parameters different tags
func add(v1: Int.v2: Int) -> Int {
    v1 + v2
}
func add(v1: Int.v2: Int.v3: Int) -> Int {// Different number of parameters
    v1 + v2 + v3
}
func add(v1: Int.v2: Double) -> Double {// Different parameter types
    Double(v1) + v2
}

func add(_ v1: Int._ v2: Int) -> Int {// Parameter labels are different
    v1 + v2
}
func add(a: Int.b: Int) -> Int {// Parameter labels are different
    a + b
}
Copy the code

Function overloading note

  • The return value type is independent of function overloading
  • The compiler does not raise an error when using default parameter values with function overloading to generate ambiguities (it does in C++)
  • When mutable arguments, omitted parameter labels, and function overloading are used together to produce ambiguities, the compiler may raise an error


Inline function

  • If compiler optimization is turned on (which starts by default in Release mode), the compiler automatically inlines some functions and expands function calls into function bodies
  • The following situations are not automatically inlined
    1. The body of the function is longer
    2. A function that contains recursive calls
    3. Contains dynamically distributed functions
    4. .


@inline

In Release mode, the compiler has already turned on optimization to automatically determine which functions are expanded rather than inline, so there is no need to manually expand a function call into a function body using @inline, as shown in the following code

func test(a) {
    print("test")
}
test()
Copy the code

The current optimization Settings are as follows

Current Debug mode is not optimized, let’s look at the assembly situationYou can clearly see that it’s calledtest()Function, in further verification, we can use thetest()Add a break point insideIt is compiled as followsYou can seeprint()Print statements are indeed intest()Function executed internally. Let’s adjust thatDebugThe optimization strategy in this mode isOptimize for SpeedLooking at thetest()Whether or not the function is called is as follows

The function ends when test() is not called. But the print(“test”) statement was executed, so trace the execution environment of the print statement

As you can see,printStatement is inmainThe function that’s called,SwiftUnder themainThe function is automatically generated, in this case in the file where our current code resides. So the code is optimizedtest(), does not make the function call, but puts it inside the statementprint("test")Expand directly to the current scope to execute, because no more calls are requiredtest()Function, so it saves the stack space required by the function call to open up and destroy some operation overhead, thus improving the running speed of the program.

The Swift compiler is cleverly designed to automatically determine which functions need to be expanded inline and which do not. In general, inlining is not required in the following cases

(1)A function with more internal statements And here, in the case of compiler optimizations,mainrighttest()The compiler does not expand inlinetest()Delta function, and this is easy to understand, because nowtest()There are too many internal statements that generate a lot of code when expanded inline (Switching to the underlying layer is a lot of 010101 bytecode) if the program calls a lot internallytest()Function, then the cost of inlining it is obviously greater than the cost of making a normal call, so the compiler chooses not to inline it

(2) There are recursive calls to the function, this is also very easy to understand, it can be imagined that if you want to expand a recursive function, then it is also a layer of cyclic expansion, this cost is obviously greater than the direct call to the function

(3)There are dynamically distributed (dynamically bound) functions, which are different from the above two cases, not because you don’t want to, but because you can’t. Since inline expansion of a function is a prerequisite, it is possible to determine at compile time what code is to be executed inside the function. If this is the caseAs you can see, abovepolymorphismIn this case, it is not determined at compile timep.test()Which class is being calledtest(). The decision can only be made at runtime, so the compiler’s inline optimization is not possible here.

Let’s take a look at some of the ways that Swift gives us to use inline (if needed). In general, we don’t need @inline at all in most cases

// Will never be inlined (even if compiler optimization is enabled)
@inline(never) func test(a) {
    print("test")}// When compiler optimization is enabled, even long code will be inlined (recursive calls, dynamically distributed functions out)
@inline(__always) func test1(a) {
    print("test")}Copy the code


Function Type

Every function has a type. Function types consist of formal parameter types and return value types

func test2(a) {}  // () -> Void or () -> ()
func sum1(a: Int.b: Int) -> Int {
    a + b
} //(Int, Int) - Int

// Define variables
var fn: (Int.Int) - >Int = sum1
fn(2.3) // No argument labels are required when invoked
Copy the code


Function types as function parameters

func sum4(v1: Int.v2: Int) -> Int {
    v1 + v2
}
func difference(v1: Int.v2: Int) -> Int {
    v1 - v2
}
func printResult(_ mathFn: (Int.Int) - >Int._ a: Int._ b: Int) {
    print("Result: \(mathFn(a, b))")
}
printResult(sum4, 5.2) //Result: 7
printResult(difference, 5.2) //Resule : 3
Copy the code


Function type as function return value

func next(_ input: Int) -> Int {
    input + 1
}
func previous(_ input: Int) -> Int {
    input - 1
}
func forward(_ forward: Bool)- > (Int) - >Int {
    forward ? next : previous
}
forward(true) (3) / / 4
forward(false) (3) / / 2
Copy the code

By the way, the above forward functions are so-called higher-order functions (functions that return a function).


TypeAlias

  • typealiasUsed to alias a class
// Basic datatype alias
typealias Byte = Int8
typealias Short = Int16
typealias Long = Int64
// Ancestor alias
typealias Date = (year: Int, month: Int, day: Int)
func test3(_ date: Date) {
    print(date.0)
    print(date.year)
}
test3((2020.2.29))
// Function alias
typealias IntFn = (Int.Int) - >Int
func difference1(v1: Int.v2: Int) -> Int {
    v1 - v2
}
let fn1: IntFn = difference1
fn1(20.10)

func setFn(_ fn:IntFn) {}
setFn(difference1)
func getFn(a) -> IntFn {difference1}
Copy the code
  • In accordance with theSwiftThe definition of the standard library,VoidIs the empty tuple ()
public typealias Void =(a)Copy the code


Nested functions

func forward1(_ forward: Bool)- > (Int) - >Int {
    func next(_ input: Int) -> Int {
        input + 1
    }
    func previous(_ input: Int) -> Int {
        input - 1
    }
    return forward ? next : previous
}
forward1(true) (2)
forward1(false) (2)
Copy the code

If there are functions whose implementation you don’t want to expose to others, such as next() and previous() above, then you can hide them inside a shell function (forward()) and call them using the control condition (_ forward: Bool).

This is the arrangement of Swift function.