函数的定义

带返回值的无参函数

  1. func num() -> Int {
  2. return 10
  3. }

带返回值的有参函数

  1. func sum(v1: Int, v2: Int) -> Int {
  2. return v1 + v2
  3. }

形参是let,也只能是let,不可以修改
无返回值函数可以简写成:

  1. func test() {
  2. }

隐式返回

如果整个函数体是一个单一表达式,那么函数会隐式返回这个表达式

  1. func sum(v1: Int, v2: Int) -> Int {
  2. v1 + v2
  3. }

返回元组:实现多返回值

  1. /// 返回前一个和后一个整数
  2. func calculate(num: Int) -> (prev: Int, next: Int) {
  3. return (num - 1, num + 1)
  4. }

函数的文档注释

函数注释需要使用以下固定规范

  1. /// 求和【概述】
  2. ///
  3. /// 求2个整数相加【更详细的描述】
  4. ///
  5. /// - Parameters: 第1个参数
  6. /// - Parameters: 第2个参数
  7. /// - Returns: 2个整数的和
  8. ///
  9. /// - Note: 传入2个整数即可【批注】
  10. ///
  11. func sum(v1: Int, v2: Int) -> Int {
  12. return v1 + v2
  13. }

image.png
苹果规范:https://swift.org/documentation/api-design-guidelines/

参数标签(Argument Label)

  1. func goToWork(at time: String) {
  2. print("time is \(time)")
  3. }
  4. goToWork(at: "09:00")

写在函数参数前面,在调用函数时,只显示参数标签,不显示参数,便于读写
也可以使用下划线_省略参数标签

  1. func sum(_ v1: Int, _ v2: Int) -> Int {
  2. v1 + v2
  3. }
  4. sum(1, 2)

默认参数值

  1. func check(name: String = "nobody", age: Int, job: String = "none") {
  2. print("name = \(name), age = \(age), job = \(job)")
  3. }
  4. check(name: "Jack", age: 20, job: "Doctor")
  5. check(name: "Rose", age: 20)
  6. check(age: 20)

参数可以添加默认值,调用时不传带默认参数值的参数
但是以下情况,v2不可以省略参数标签:

  1. func sum(_ v1: Int = 10, _ v2: Int, _ v3: Int = 30) -> Int {
  2. v1 + v2 + v3
  3. }
  4. sum(10)

image.png
当调用时,只传入一个参数,默认是给v1赋值,导致v2没有被赋值,造成报错。

可变参数(Variadic Parameter)

  1. func sum(_ numbers: Int...) -> Int {
  2. var sum = 0
  3. for num in numbers {
  4. sum += num
  5. }
  6. return sum
  7. }
  8. sum(10, 20, 30)

一个函数最多只能有1个可变参数
紧跟在可变参数后面的参数不能省略参数标签,避免调用时的歧义

  1. func test(_ numbers: Int..., v1: Int) {}
  2. test(10, v1: 20)

print

  1. /// - Parameters:
  2. /// - items: Zero or more items to print.
  3. /// - separator: A string to print between each item. The default is a single
  4. /// space (`" "`).
  5. /// - terminator: The string to print after all items have been printed. The
  6. /// default is a newline (`"\n"`).
  7. public func print(_ items: Any..., separator: String = " ", terminator: String = "\n")

print方法第一个参数是可变参数,调用时可以传入多个打印对象

  1. print("1", "2", "3")

第二个参数是separator,打印对象间的间隔符,默认是空格
第三个参数是terminator,是打印结尾追加的字符,默认是回车

  1. print("1", "2", "3", separator: "_", terminator: "---------")

打印结果:

  1. 1_2_3---------

输入输出参数(In-Out Parameter)

可以使用inout定义一个参数,可以在函数内部修改参数

  1. var number = 10
  2. func add1(_ num: inout Int) {
  3. num += 1
  4. }
  5. add1(&number)
  6. print("number = \(number)")

inout参数在调用时,需要使用&修饰,可以理解为地址传递
通过查看汇编代码可知:
image.png
是通过lea指令,将number的地址传入add1函数,再通过mov指令,给number的地址赋值。
特点:
可变参数不能标记为inout
inout参数不能有默认值
inout参数的本质是地址传递(引用传递)
inout参数只能传入可以被多次赋值的

函数重载(Function Overload)

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

  1. func sum(v1: Int, v2: Int) -> Int {
  2. v1 + v2
  3. }
  4. /// 参数个数不同
  5. func sum(v1: Int, v2: Int, v3: Int) -> Int {
  6. v1 + v2 + v3
  7. }
  8. /// 参数类型不同
  9. func sum(v1: Double, v2: Double) -> Double {
  10. v1 + v2
  11. }
  12. /// 参数标签不同
  13. func sum(a v1: Int, b v2: Int) -> Int {
  14. v1 + v2
  15. }

注意点:
返回值类型与函数重载无关
image.png
默认参数值和函数重载一起使用产生二义性时,编译器并不会报错(在c++中会报错)
可变参数、省略参数标签、函数重载一起使用产生二义性时,编译器可能会报错
image.png

內联函数

如果开启了编译器优化(Release模式默认会开启优化),编译器会自动将某些函数变成内联函数
image.png
内联函数就是将函数展开为函数体,例如下面代码,方法内部比较简单:

  1. func test() {
  2. print("test")
  3. }
  4. test()

通过内联优化,可以优化为:

  1. print("test")

减少开销。
如果函数内部比较复杂,且调用次数较多,则不会内联
如果是递归函数,则不会内联
如果包含动态派发,则不会内联

@inline

  1. // 永远不会被内联(即使开启了编译器优化)
  2. @inline(never) func test() {
  3. print("test")
  4. }
  1. // 开启编译器优化后,即使代码很长,也会被内联(递归调用函数、动态派发的函数除外)
  2. @inline(__always) func test() {
  3. print("test")
  4. }

在Release模式下,编译器已经开启优化,会自动决定哪些函数需要内联,因此没必要使用@inline

函数类型(Function Type)

每一个函数都是有类型的,函数类型由形式参数类型、返回参数类型组成

  1. func test() {}
  2. // 函数类型 () -> Void 或者 () -> ()
  1. func sum(a: Int, b: Int) -> Int {
  2. a + b
  3. }
  4. // 函数类型: (Int, Int) -> Int

可以通过函数类型去定义变量

  1. var fn: (Int, Int) -> Int = sum
  2. fn(2, 3) // 5, 调用时不需要参数标签

函数类型可以作为函数参数

  1. /// 求和
  2. func sum(a: Int, b: Int) -> Int {
  3. a + b
  4. }
  5. /// 求差
  6. func difference(a: Int, b: Int) -> Int {
  7. a - b
  8. }
  9. func result(_ fn: (Int, Int) -> Int, _ a: Int, _ b: Int) {
  10. print("result = \(fn(a, b))")
  11. }
  12. result(sum, 10, 20) // 30
  13. result(difference, 10, 20) // -10

result函数的第一个参数fn是函数类型(Int, Int) -> Int,调用时可以将函数作为参数传入

函数类型作为函数返回值

  1. func next(_ input: Int) -> Int {
  2. input + 1
  3. }
  4. func previous(_ input: Int) -> Int {
  5. input - 1
  6. }
  7. func forward(_ forward: Bool) -> (Int) -> Int {
  8. forward ? next : previous
  9. }
  10. forward(true)(3) // 4
  11. forward(false)(3) // 2

forward(true)的结果是一个函数,可以用这个函数继续传入一个参数进行计算,forward(true)(3)的结果等于4
返回值是函数类型的函数,叫做高阶函数(Heigh-Order Function)

typealias

typealias用来给类型起别名

  1. /// 给类型起别名
  2. typealias Byte = Int8
  3. typealias Short = Int16
  4. typealias Long = Int64
  5. /// 给元组起别名
  6. typealias Date = (year: Int, month: Int, day: Int)
  7. /// 给函数类型起别名
  8. typealias IntFn = (Int, Int) -> Int

按照Swift标注库的定义,Void就是空元组 ()

  1. public typealias Void = ()

嵌套函数(Nested Function)

将函数定义在函数内部

  1. func forward(_ forward: Bool) -> (Int) -> Int {
  2. func next(_ input: Int) -> Int {
  3. input + 1
  4. }
  5. func previous(_ input: Int) -> Int {
  6. input - 1
  7. }
  8. return forward ? next : previous
  9. }