Swift函数

函数的定义

  • 形参默认是let,也只能是let
func pi() -> Double {
    return 3.14
}

func sum(v1: Int, v2: Int) -> Int {
    return v1 + v2
}
  • 如果函数没有返回值,有三种写法
  • 返回Void
  • 返回空元组
  • 省略不写
func sayHello() -> Void { 
    print("Hello")
}

func sayHello() -> () {
    print("Hello")
}

func sayHello() {
    print("Hello")
}

隐式返回

  • 如果整个函数体是一个单一表达式,那么函数会隐式返回这个表达式,不用写return
  • 如果有其他代码,不许要写return
func sum(v1: Int, v2: Int) -> Int {
    v1 + v2
}

返回元组(实现多返回值)

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: 10, v2: 20)
result.sum // 30
result.difference // -10
result.average // 15

求平均数:和右移1为
110 >> 1   -->  11
6       -->   3

参数标签

  • 可以修改参数标签,合理的参数标签可以使代码的可读性大大增强
func goToWork(at time: String) {
    print("This time is \(time)")
}

goToWork(at: "08:00")

// The time is 08:00
  • 使用_省略参数标签
func sum(_ v1: Int, _ v2: Int) -> Int {
    v1 + v2
}
  • 有参数标签和省略参数标签的区别

默认参数

  • 参数可以有默认值
func check(name: String = "nobody", age: Int, job: String = "none") {
    print("name=\(name), age=\(age), job=\(job)")
}
check(name: "Jack", age: 20, job: "Doctor")  // name=Jack, age=20, job=Doctor
check(name: "Rose", age: 18) // name=Rose, age=18, job=none
check(age: 10, job: "Batman") // name=nobody, age=10, job=Batman
check(age: 15) // name=nobody, age=15, job=none
  • Swift中的函数 设置默认参数没有顺序要求,因为Swift拥有参数标签,可以明确为某个参数传参
  • 但是,如果想通过 _省略参数标签并设置默认值,同时有的参数没有默认值,那么没有默认值的参数,不能省略参数标签(建议:尽量不要省略参数标签)
  • 省略参数标签和设置默认值最好不要一起使用。非常容易产生问题
// 如果middle前也通过下划线_来省略参数标签,那么在函数调用时候,传一个参数,编译器不知道参数值该赋值给哪个参数,就会报错
func test( _ first: Int = 10, middle: Int, _ last: Int = 30) {}

可变参数

  • 一个函数最多只能有1个可变参数
  • 紧跟在可变参数后面的参数不能省略参数标签
func sum(_ numbers: Int...) -> Int {
    var total = 0
    for num in numbers {
        total += num
    }
    return total
}
sum(10, 20, 30, 40) // 100
  • string不能省略参数标签,如果省略可能会产生歧义,比如省略string参数标签,换成Int类型就会产生歧义
func test2(_ numbers: Int..., string: String, _ other: String) {}
test2(1, 2, 3, string: "Anna", "Bob")

Swift自带的print函数

  • print函数是默认参数和可变参数的结合使用
  • items参数是一个可变参数,可以传入多个Any类型的值
  • separator默认每个item之间用空格分隔
  • terminator默认最后一个字符是换行符
/// - 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(_ items: Any..., separator: String = " ", terminator: String = "\n")
  • 根据print函数的特点,我们就可以灵活使用print函数了。
print(1, 2, 3, 4, 5)
print(1, 2, 3, 4, 5, separator: "_")
print("My name is Jack.", terminator: "")
print("My age is 17")

// 打印结果
1 2 3 4 5
1_2_3_4_5
My name is Jack.My age is 17

输入输出参数

  • 可以用inout定义一个输入输出参数:可以在函数内部修改外部实参的值
  • 可变参数不能标记为inout
  • inout参数不能有默认值
  • inout参数只能传入可以被多次赋值的
  • inout参数的本质是地址传递(引用传递)
func swapValues(_ v1: inout Int, _ v2: inout Int) {
    let temp = v1
    v1 = v2
    v2 = temp
}

var num1 = 10
var num2 = 20
swapValues(&num1, &num2)
num1 // 20
num2 // 10

// 使用元组实现2个数字交换
func swapValues(_ v1: inout Int, _ v2: inout Int) {
    (v1, v2) = (v2, v1)
}

swapValues(&num1, &num2)
num1 // 20
num2 // 10

可变参数不能标记为inout
inout参数不能有默认值
inout参数的本质是地址传递(引用传递)

通过汇编验证inout本质是地址传递

  • 创建命令行项目,写如图代码,并设置断点,运行程序

  • Debug -> Debug Workflow -> Always Show Disassembly 打勾

  • 如图,打断点的位置就是在调用add这个函数,call后面即函数的地址(0x100000b70)

  • 红色框中的leaq指令在汇编中即地址传递,将(0x622 + rip) 赋值给 rdi ,rdi存储着参数的地址,即number的地址。

  • 进入add函数的汇编指令

  • 红框内,(rdi) 即根据 rdi中存储的地址值,找到对应的存储空间,将20放到存储空间中

函数重载

  • 规则
  • 函数名相同
  • 参数个数不同 || 参数类型不同 || 参数标签不同

```
// 参数个数不同
func sum(v1: Int, v2: Int) {
v1 + v2
}

func sum(v1: Int, v2: Int, v3: Int) {
v1 + v2 + v3
}
sum(v1: 10, v2: 20, v3: 30)

// 参数类型不同
func sum(v1: Double, v2: Int) -> Double {

top Created with Sketch.