function

Definition of a function

A function that returns a value

The default parameter is let and can only be let

func sum(v1: Int, v2: Int) -> Int { 
    return v1 + v2 
}
Copy the code

A function that returns no value

Essentially an empty tuple returns the value

SayHello () -> Void {} sayHello() -> () {} sayHello() {} sayHello() {} sayHello();Copy the code

Implicit return

If the whole function body is a single expression, the function implicitly returns that expression

func sum(v1: Int, v2: Int) -> Int { v1 + v2 }

sum(v1: 10, v2: 20)
Copy the code

Returns a tuple that implements multiple return values

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)
print(result.sum, result.difference, result.average)
Copy the code

Documentation of the function

Notes can be written in a certain format for easy reading

/// /// -parameter v1: the first integer /// -parameter v2: the second integer /// -parameter v2: the second integer /// /// /// - Note: func sum(v1: Int, v2: Int) -> Int {v1 + v2}Copy the code

Refer to Apple’s official API design guidelines in detail

Argument Label

You can modify the parameter label

func gotoWork(at time: String) {

}

gotoWork(at: "8:00")
Copy the code

Parameter labels can be omitted, but are generally not recommended for readability

func sum(_ value1: Int, _ value2: Int) -> Int {
	value1 + value2
}

sum(5, 5)
Copy the code

Default Parameter Value

Parameters can have 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: "Doctor")
check(name: "Jack", age: 20)
check(age: 20, job: "Doctor")
check(age: 20)
Copy the code

C++ default parameter values have a limitation: they must be set from right to left; Because Swift has parameter labels, there are no such restrictions

However, you need to be careful when omitting parameter labels to avoid errors

Func test(_ first: Int = 10, middle: Int, _ last: Int = 30) {} test(middle: 20)Copy the code

Variadic Parameter

A function can have at most one variable argument

func sum(_ numbers: Int...) -> Int {
	numbers.count
}

sum(1, 2, 3, 4)
Copy the code

Parameters immediately following a variable parameter cannot omit the parameter label

Func get(_ number: Int... , string: String, _ other: String) { } get(10, 20, string: "Jack", "Rose")Copy the code

Take a look at Swift’s print function

print(1, 2, 3, 4, 5)
print(1, 2, 3, 4, 5, separator: " ", terminator: "\n")
Copy the code

In-out Parameter

You can use inout to define an input and output parameter: you can modify the values of external arguments inside the function

func swapValues(_ v1: inout Int, _ v2: inout Int) {
	let tmp = v1
	v1 = v2
	v2 = tmp
}

var num1 = 10
var num2 = 20
swapValues(&num1, &num2)
Copy the code

The official swap function is inout

You can use tuples to exchange parameters

func swapValues(_ v1: inout Int, _ v2: inout Int) {
	(v1, v2) = (v2, v1)
}

var num1 = 10
var num2 = 20
swapValues(&num1, &num2)
Copy the code

Variable arguments cannot be marked as inout

The inout parameter cannot have a default value

An inout argument can only be passed in that can be assigned multiple times

Constants can only be assigned once when they are defined, so an error is reported below

The nature of the inout argument is address passing

Let’s create a new project and disassemble it to see its essence

Leaq stands for address passing. You can see that the addresses of the two variables are placed in registers before calling the function

Function Overload

Rules for function overloading

  • Same function name
  • Parameter number different | | parameter types different | | parameters different tags
Func sum(value1: Int, value2: Int) -> Int {} // Different number of arguments func sum(_ value1: Int, _ value2: Int, _ value3: Int sum(_ a: Int, _ b: Int) -> Int {} func sum(_ a: Int, _ b: Int) -> Int {} Double) -> Int { }Copy the code

Return value types are 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++)

Func sum(_ value1: Int, _ value2: Int, _ value3: Int = 5) -> Int {v1 + v2 + v3} func sum(_ value1: Int, _ value2: Int, _ value3: Int = 5) -> Int {v1 + v2 + v3} Int, _ value2: Int) -> Int {v1 + v2}Copy the code

When mutable arguments, omitted parameter labels, and function overloading are used together to produce ambiguities, the compiler may raise an error

Inline functions

If compiler optimization is enabled (which is enabled by default in Release mode), the compiler will automatically make some functions inline

What does the compiler do to change tuning options in Debug mode

1. Let’s create a new project. The project code is as follows

2. We then disassemble to see what the compiler does when it is not optimized

As you can see, the test function is called, and then the print is performed inside the test function

3. Now let’s turn on optimization options in the Debug model and run the program

It was found that print was directly executed in main function without the call process of test function

This is equivalent to putting print directly inside main, and the compiler expands the function call into the function body

Which functions are not inlined

  • The body of the function is longer
  • Contains recursive calls
  • Includes dynamic dispatch (polymorphic calls at runtime)

We can use@inlineKeyword to actively control whether the compiler does optimizations

@inline(nerver) : will never be inline, even if compiler optimization is turned on

@inline(nerver) func test() {}
Copy the code

@inline(__alaways) : When compiler optimization is enabled, long code will be inlined (except for recursive calls and dynamic dispatch)

@inline(__alaways) func test() {}
Copy the code

In Release mode, the compiler already has optimization enabled and will automatically determine which functions need to be inline, so @inline is not necessary

Function Type

Every function has a type. A function type consists of a parameter type and a return value type

/ / () - > Void or () - > () func test () {} / / (Int, Int) - > Int func sum (a: Int, b: Int) - > Int {} / / define variables var fn: (Int, Int) -> Int = sum fn(5, 3Copy the code

Function types as function parameters

func sum(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) {
	mathFn(a, b)
}

printResult(sum, 5, 2)
printResult(difference, 5, 2)
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)
forward(false)(3)
Copy the code

A function whose return value is a function type is called a higher-order function

typealias

Used to alias a type

typealias Byte = Int8
typealias Short = Int16
typealias Long = Int64

typealias Date = (year: String, mouth: String, day: String)
func getDate(_ date: Date) {
    print(date.day)
    print(date.0)
}

getDate(("2011", "9", "10"))


typealias IntFn = (Int, Int) -> Int

func difference(v1: Int, v2: Int) -> Int {
    v1 - v2
}

let fn: IntFn = difference
fn(20, 10)

func setFn(_ fn: IntFn) { }
setFn(difference)

func getFn() -> IntFn { difference }
Copy the code

The Swift library defines Void as an empty tuple ()

Nested function

Define a function inside a function

func forward(_ forward: Bool) -> (Int) -> Int {
	func next(_ input: Int) -> Int {
		input + 1
	}
	
	func previous(_ input: Int) -> Int {
		input - 1
	}

	forward ? next : previous
}

forward(true)(3)
forward(false)(3)
Copy the code

(Optional)

Optional, also commonly called optional types, allow values to be set to nil

Put a question mark after the type name? To define an optional

var name: String? = nil
Copy the code

If the optional type is defined without a given value, the default value is nil

var age: Int? Equivalent to var age: Int? = nilCopy the code

If the Optional type is assigned when it is defined, it is an Optional value

var name: String? = "Jack" // Optional(Jack)
Copy the code

Optional types can also be used as function return values

var array = [1, 2, 3, 4]

func get(_ index: Int) -> Int? {
    if index < 0 || index >= array.count {
        return nil
    }
    
    return array[index]
}
Copy the code

Forced Unwrapping

Optional is a layer of packaging on other types, which can be understood as a box

  • If it isnilSo it’s an empty box
  • If not fornilThe box contains the type of data to be wrapped
var age: Int?
age = 10
age = nil
Copy the code

The types of optional relationships are roughly shown below

If you want to take wrapped data out of the options (take the contents of the box out), use an exclamation point! Forced unpack

var age: Int? = 10 var ageInt = age! AgeInt += 10 // ageInt is an IntCopy the code

Forced unpacking of an optional (empty box) with a value of nil will result in a runtime error

Optional Binding

We can determine if the optional contains a value

Let number = Int("123") // number = Int? if number ! = nil { print(number!) }Copy the code

Optional bindings can also be used to determine whether optional options contain values

If it does, it is automatically unpacked, assigns the value to a temporary constant (let) or variable (var), and returns true, otherwise returns false

If let number = Int("123") {print(" \(number)") // number is forced to unpack the Int value // number scope is limited to this curly bracket} else {print(" string to integer failed ")}Copy the code

If there are multiple criteria, you can combine them and use commas to separate them

if let first = Int("4") { if let second = Int("42") { if first < second && second < 100 { print("\(first) < \(second) < Let first = Int("4"), let second = Int("42"),  first < second && second < 100 { print("\(first) < \(second) < 100") }Copy the code

Optional bindings are used in the while loop

let strs = ["10", "20", "abc", "-20", "30"]

var index = 0
var sum = 0
while let num = Int(strs[index]), 
		num > 0 {
		sum += num
		index += 1
}
Copy the code

The Nil-Coalescing Operator

We can use the null merge operator?? To check if the previous value has a value, and if the previous value is nil, it returns the later value

Detailed usage is as follows

a ?? B is optional b is optional or not optional B has to be of the same storage type as A if a is not nil return A if a is nil return B if B is not optional return A and it will be unpacked automaticallyCopy the code

The type of results depends on?? What is the following value type

let a: Int? = 1 let b: Int = 2 let c = a ?? B // c is Int, 1 let a: Int? = nil let b: Int = 2 let c = a ?? B // c is Int, 2Copy the code

More than one?? Used together

let a: Int? = 1
let b: Int? = 2
let c = a ?? b ?? 3 

let a: Int? = nil
let b: Int? = 2
let c = a ?? b ?? 3
Copy the code
var a: Int??? = 10
var b: Int = 20
var c: Int? = 30

print(a ?? b) // Optional(Optional(10))
print(a ?? c) // Optional(Optional(10))
Copy the code

?? Used in conjunction with if let

let a: Int? = nil
let b: Int? = 2
if let c = a ?? b {
	print(c)
}

if let c = a, let d = b {
	print(c)
	print(d)
}
Copy the code

Guard statement

When the guard statement condition is false, the code inside the braces is executed

When the guard statement condition is true, the guard statement is skipped

The GUARD statement is suitable for “early exit”

Guard condition else {// do something.... Exit current scope // return}Copy the code

When guard statements are used for optional binding, bound constants (lets) and variables (var) can also be used in outer scopes

func login(_ info: [String : String]) {guard let username = info["username"] else {print(" please input username") return} Guard let password = info["password"] Else {print(" Please enter password ") return} // if username.... // if password .... Print (username: \ "(username)", "password: \ (password)," login "ing")}Copy the code

Implicitly Unwrapped Optional

In some cases, an option will always have a value once it has been set

In this case, the checking can be removed, and it does not have to be unpacked every time it is accessed, because it can be sure that it has a value every time it is accessed

You can put an exclamation mark after the type! Defines an implicit unpacking option

let num1: Int! = 10 let num2: Int = num1 if num1 ! = nil { print(num1 + 6) } if let num3 = num1 { print(num3) }Copy the code

An error is also reported if an option with a null value is implicitly unpacked

Optional types of implicit unpacking, most want someone to give a value that is not null, if someone passed a null value that is an error, the purpose is to make your rules, more suitable for making an interface to receive arguments;

It is recommended not to use this type

String interpolation

Optional The compiler will warn if the string is interpolated or printed directly

There are at least three ways to eliminate warnings

var age: Int? = 10 print("My age is \(age!) ") // My age is 10 print("My age is \(String(describing: age))") // My age is Optional(10) print("My age is \(age ?? 0)") // My age is 10Copy the code

Multiple options

1. Look at the following optional types, which can be interpreted using the following pictures

var num1: Int? = 10
var num2: Int?? = num1
var num3: Int?? = 10

print(num2 == num3) // true
Copy the code

Use the LLDB directive frame variable-r or fr V-r to check the difference

2. Look at the following optional types, which can be interpreted using the following pictures

var num1: Int? = nil var num2: Int?? = num1 var num3: Int?? = nil print(num2 == num3) // false print(num3 == num1) // false (because the type is different) 1)?? 2 // 2 (num3 ?? 1)?? 2 / / 1Copy the code

No matter how many layers are optional, once you assign nil, there’s only one big outer box

Use the LLDB directive frame variable-r or fr V-r to check the difference