Swift编程指南——函数

262 阅读5分钟

1. 函数

1.1 函数的定义

  • 形参默认是let,也只能是let
func pi() -> Double{
    return 3.14
}
func sum(v1:Int,v2:Int) -> Int{
    return v1+v2
}
sum(v1: 10, v2: 20)
  • 无返回值
func sayHello() -> Void{
    print("Hello")
}
func sayHello2() -> (){
    print("Hello")
}
func sayHello3(){
    print("Hello")
}

1.2 隐式返回

  • 如果整个函数是一个单一的表达式,那么函数会隐式返回这个表达式
func sum2(v1:Int,v2:Int) -> Int{
    v1+v2
}
sum(v1: 10, v2: 20)

1.3 返回元组

  • 实现多返回值
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

1.4 函数的文档注释

/// 求和【概述】
/// 将2个整数相加【更详细的描述】
/// - Parameter v1: 第1个整数
/// - Parameter v2: 第2个整数
/// - Returns: 2个整数的和
/// - Note:传入2个整数即可【批注】
///
func sum3(v1:Int,v2:Int) -> Int{
    v1+v2
}
//参考:https://swift.org/documentation/api-design-guidelines/

1.5 参数标签

  • 可以修改参数标签
func goToWork(at time:String){
    print("this time is \(time)")
}
goToWork(at: "08:00") //this time is 08:00
  • 可以使用下划线省略参数标签
func sum(_ v1:Int,_ v2:Int) -> Int{
    v1 + v2
}
sum(10,20)

1.6 默认参数值

  • 参数可以有默认值
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: "Rose", age: 18)
check(age: 10, job: "Batman")
check(age: 15)
  • C++的默认参数必须从右往左设置。C++的默认参数必须从右往左设置。由于swift拥有参数标签,并没有此类限制。但是在省略参数标签时,需要特别注意出错。
func test(_ first:Int = 10,middle:Int = 20,_ last:Int = 30){}
test(middle:20)

1.7 可变参数

func sum4(_ numbers:Int...) -> Int{
    var total = 0
    for number in numbers {
        total += number
    }
    return total
}
sum4(10,20,30,40)//100
  • 一个函数最多只能有一个可变参数,紧跟在可变参数后面的参数不能省略参数标签
func test(_ numbers:Int...,string:String,_ other: String){}//参数string不能省略标签
test(10,20,30,string:"Jack","Rose")
print(1,2,3,4,5)//1 2 3 4 5
print(1,2,3,4,5,separator:"_")//1_2_3_4_5

1.8 输入输出参数

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

1.9 函数重载

  • 函数名相同,函数参数不同||参数类型不同||参数标签不同
func sum(v1:Int,v2:Int) -> Int{
    v1+v2
}

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

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

func sum(_ v1:Int,_ v2:Int) -> Int{
    v1+v2
}//参数标签不同
func sum(a:Int,b:Int) -> Int{
    a+b
}//参数标签不同
  • 注意:返回值类型和函数重载无关
  • 注意:默认参数值和函数重载一起使用产生二义性时,编译器并不会报错(c++中会报错)
func sum(v1:Int,v2:Int) -> Int{
    v1+v2
}
func sum(v1:Int,v2:Int,v3:Int = 10) -> Int{
    v1+v2+v3
}
sum(v1: 10, v2: 20)//会调用sum(v1:Int,v2:Int)
  • 注意:可变参数/省略参数标签,函数重载一起使用产生二义性时,编译器可能会报错

1.10 内联函数

  • 如果开启了编译器优化(Release模式会默认开启优化),编译器会自动将某些函数变为内联函数,将函数的调用展开成函数体
  • 不会被自动内联的情况:函数体比较长,包含递归调用,包含动态派发
  • inLine
@inline(never) func test1(){print("test")}//永远不会被内联(即使开启了编译器优化)
@inline(__always) func test2(){print("test")}//开启编译器优化后,即使代码很长,也会被内联(递归调用函数,动态派发函数除外)
  • 在Release模式下,编译器已经开始优化,会自决定哪些函数需要被内联,因此没必要使用@inline

1.11 函数类型

  • 每一个函数都是有类型的,函数类型由形式参数类型,返回值类型组成
func test3(){} //()->Void 或者 ()->()
func sum(x:Int,y:Int)->Int{x+y} //(Int,Int)->Int
var fn:(Int,Int)->Int = sum
fn(2,3)
  • 函数类型作为函数参数
func sum(v1:Int,v2:Int) -> Int{ v1+v2 }
func difference(v1:Int,v2:Int) -> Int{ v1-v2 }
func printResult(_ mathFunc:(Int,Int)->Int,_ a:Int,_ b:Int){
    print("Result:\(mathFunc(a,b))")
}
printResult(sum, 5, 2)//Result:7
printResult(difference(v1:v2:), 5, 2)//Result:3
  • 函数类型作为函数返回值。返回值是函数类型的函数,叫做高阶函数
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

1.11 别名typealias

  • typealias用来给类型起别名
typealias Byte = Int8
typealias Short = Int16
typealias Long = Int64

typealias Date = (year:Int,month:Int,day:Int)
func test(_ date:Date){
    print(date.0)
    print(date.year)
}
test((2011,9,10))

typealias IntFn = (Int,Int) -> Int
func difference(v1:Int,v2:Int) -> Int{
    v1 - v2
}
let fn:IntFn = difference(v1:v2:)
fn(20,10)

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

func getFn() -> IntFn{difference(v1:v2:)}
  • 源码可以看出,按照swift标准库的定义, Void就是空元组
public typealias Void = ()

1.12 嵌套函数

  • 将函数定义在函数内部
func forward(_ forward:Bool)-> (Int) -> Int{
    func next(_ input:Int) -> Int{
        input + 1
    }
    
    func previous(_ input:Int) -> Int{
        input - 1
    }
    return forward ? next : previous(_:)
}
forward(true)(3)//4
forward(false)(3)//2