函数在任何语言中都是存在的,Swift中函数更加灵活。
一、函数的定义
1.1. 有返回值(形参默认是let,也只能是let)
1 | func pi() -> Double { |
1.2. 无返回值
1 | func hello() -> Void { |
1.3. 如果整个函数体是一个单一表达式,那么函数会隐式返回这个表达式
1 | func sum(v1: Int, v2: Int) -> Int { |
1.4. 返回元组:实现多返回值
1 | func calculate(v1: Int, v2: Int) -> (sum: Int, difference: Int, average: Int) { |
1.5. 参数标签
函数可以修改参数标签:
1 | // at是外面调用的,time是函数内部使用的 |
可以使用下划线_
省略参数标签:
1 | func sum(_ v1: Int, _ v2: Int) -> Int { v1 + v2} |
1.6. 默认参数值
- 参数可以有默认值
1
2
3
4
5
6
7
8
9
10
11
12
13
14func check(name: String = "nobody", age: Int, job: String = "none") {
print("name=\(name), age=\(age), job=\(job)")
}
check(age: 10)
check(name: "Jack", age: 20, job: "Programmer")
check(name: "Eve", age: 18)
check(age: 10, job: "Superman")
/*
输出:
name=nobody, age=10, job=none
name=Jack, age=20, job=Programmer
name=Eve, age=18, job=none
name=nobody, age=10, job=Superman
*/ - C++的默认参数有个限制:必须从右往左设置,而且不能跨标签;Swift拥有参数标签,因此没有此类限制
- 省略参数标签时,需要特别注意,避免出错
- 没有默认值的参数标签不能省略(如上面代码age就不能省略)
1.7. 可变参数
一个函数最多只能有1个可变参数
1
2
3
4
5
6
7
8
9func sum(_ numbers: Int...) -> Int {
var total = 0
for number in numbers {
total += number
}
return total
}
let result = sum(10, 20, 30, 40)
print(result) // 输出:100紧跟在可变参数后面的参数不能省略参数标签(思考:如果省略了会怎么样?)
1
2
3
4func test(_ numbers: Int..., string: String, _ other: String) {
}
test(10, 20, 30, string: "idbeny", "1024星球")
1.8. 输入输出参数
- 可以用inout定义一个输入输出参数:可以在函数内部修改外部实参的值
inout
必须和&
配合使用- 本质上是地址传递
- 可变参数不能标记为
inout
inout
参数不能有默认值inout
参数只能传入可以被多次赋值的
如下修改会报错(因为形参是用let修饰的)
使用inout
和&
1 | var number = 10 |
通过这种方式也可以交换两个变量的值(其实官方有提供交换函数swap
)
1 | // 方法一(使用临时变量) |
二、函数的文档注释
官网介绍:https://swift.org/documentatior/api-design-guidelines
光标放到对应函数名称上,按下Option
即可查看函数相关描述文档
- 上图看到sum的函数描述都是空的,如何填写描述信息呢?
- 添加文档快捷键(光标一定要在函数上或函数上方):
Command + Option + /
默认的文档注释
1 | /// <#Description#> |
更详细的文档注释(概述和详述一定要隔开,否则会默认为详述)
1 | /// 将2个整数相加【概述】 |
效果
三、函数重载
- OC是不支持函数重载的,但Swift支持函数重载
- 规则
- 函数名相同
- 参数个数不同 || 参数类型不同 || 参数标签不同
以下函数都构成了函数重载,都是可以正常调用的,且函数没有冲突:
1 | // 参照 |
返回值类型与函数重载无关
默认参数值和函数重载一起使用产生二义性时,编译器不会报错(在C++中会报错)
1
2
3
4
5
6
7
8
9func sum(v1: Int, v2: Int) -> Int {
v1 + v2
}
func sum(v1: Int, v2: Int, v3: Int = 30) -> Int {
v1 + v2 + v3
}
sum(v1: 10, v2: 20) // 输出:30可变参数、省略参数标签,函数重载一起使用产生二义性时,编译器有可能会报错
为什么是可能会报错?(下面代码可以正常执行,所以平时开发中也不建议这样写)
1 | func sum(_ v1: Int, _ v2: Int) -> Int { |
四、函数类型
每一个函数都是有类型的,函数类型由形式参数类型,返回值类型组成:
- 函数类型可以作为函数参数传递;
- 函数类型可以作为函数返回值;
- 返回值是函数类型的函数,叫做高阶函数。
五、嵌套函数
将函数定义在函数内部:
1 | func foward(_ forward: Bool) -> (Int) -> Int { |
六、内联函数
如果开启了编译器优化(Release模式默认开启),编译器会自动将某些函数变成内联函数:
- Release默认按照快速运行方式优化;
- Debug也可以手动开启,但是为了开发中方便调试,一般都会关闭。
内联函数的作用?
- 内联函数在C++中的经常出现,如果某一个函数是内联函数,编译器在编译的时候会把适当的代码块中的代码放到对应位置,提高代码执行的效率
- 将函数调用展开成函数体
1
2
3
4func test() {
print("test")
}
test() - 以上代码如果开启编译器优化,
test()
会替换为print("test")
。
不是所有的内联函数都会被编译器展开函数体,那些函数不会被内联?
- 函数体比较长
- 包含递归调用
- 包含动态派发
@inline:
永远不会被内联(即使开启了编译器优化)
1 | func test() { (never) |
开启编译器优化后,及时代码很长,也会被内联(递归调用、动态派发的函数除外):
1 | func test1() { (__always) |
- 在Release模式下,编译器已经开启优化,会自动决定哪些函数需要内联,因此没必要使用
@inline
。