文章目录
- [Swift - 函数](#Swift - 函数)
-
- [1. 函数的定义](#1. 函数的定义)
- [2. 隐式返回(Implicit Return)](#2. 隐式返回(Implicit Return))
- [3. 返回元组:实现多返回值](#3. 返回元组:实现多返回值)
- [4. 函数的文档注释](#4. 函数的文档注释)
- [5. 参数标签(Argument Label)](#5. 参数标签(Argument Label))
- [6. 默认参数值(Default Parameter Value)](#6. 默认参数值(Default Parameter Value))
- [7. 可变参数(Variadic Parameter)](#7. 可变参数(Variadic Parameter))
- [8. Swift自带的print函数](#8. Swift自带的print函数)
- [9. 输入输出参数(In-Out Parameter)](#9. 输入输出参数(In-Out Parameter))
- [10. 函数重载(Function Overload)](#10. 函数重载(Function Overload))
-
- [10.1 规则](#10.1 规则)
- [10.2 函数重载注意点](#10.2 函数重载注意点)
-
- [10.2.1 `返回值`类型与函数重载`无关`](#10.2.1
返回值
类型与函数重载无关
) - [10.2.2 `默认参数值`和`函数重载`一起使用产生`二义性`时,编译器并`不会报错`(在C++中会报错)](#10.2.2
默认参数值
和函数重载
一起使用产生二义性
时,编译器并不会报错
(在C++中会报错)) - [10.2.3 `可变参数`、`省略参数标签`、`函数重载`一起使用产生`二义性`时,编译器有可能会报错](#10.2.3
可变参数
、省略参数标签
、函数重载
一起使用产生二义性
时,编译器有可能会报错)
- [10.2.1 `返回值`类型与函数重载`无关`](#10.2.1
- [11. 内联函数](#11. 内联函数)
-
- [11.1 如果开启了编译器优化(Release模式默认会开启优化),编译器会自动将某些函数变成内联函数](#11.1 如果开启了编译器优化(Release模式默认会开启优化),编译器会自动将某些函数变成内联函数)
- [11.2 哪些函数不会被自动内联?](#11.2 哪些函数不会被自动内联?)
- [12. @inline](#12. @inline)
- [13. 函数类型(Function Type)](#13. 函数类型(Function Type))
- [14. 函数类型作为函数参数](#14. 函数类型作为函数参数)
- [15. 函数类型作为函数返回值](#15. 函数类型作为函数返回值)
- [16. typealias](#16. typealias)
- [17. 嵌套函数](#17. 嵌套函数)
Swift - 函数
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")
}
可省略Void
:
func sayHello() -> () {
print("Hello")
}
可再精简:
func sayHello() {
print("Hello")
}
2. 隐式返回(Implicit Return)
如果整个函数体是一个单一表达式,那么函数会隐式返回这个表达式
// 隐式返回
func sum(v1: Int, v2: Int) ->Int {
v1 + v2
}
sum(v1: 10, v2: 20)
3. 返回元组:实现多返回值
// 返回元组:实现多返回值
func calculate(v1: Int, v2: Int) -> (sum: Int, difference: Int, average: Int) {
let sum = v1 + v2
return (sum, v1 - v2, sum >> 1) // (v1 + v2) >> 1 等价于 (v1 + v2) \ 2
}
let result = calculate(v1: 20, v2: 10)
result.sum // 30
result.difference // 10
result.average // 15
4. 函数的文档注释
按住
option
,出现?
,然后鼠标点击,呼出文档注释
添加文档注释
/// 求和【概述】
///
/// 将2个整数相加【更详细的描述】
///
/// - Parameter v1: 第1个整数
/// - Parameter v2: 第2个整数
/// - Returns: 2个整数的和
///
/// - Note:传入2个整数即可【批注】
///
func sum(v1: Int, v2: Int) ->Int {
v1 + v2
}
效果:
更多详情可参考:https://swift.org/documentation/api-design-guidelines/
5. 参数标签(Argument Label)
修改参数标签
方法里面使用参数time
func goToWork(at time: String) {
print("this time is \(time)")
}
调用时,看到的参数名是at
这样做好处是,从函数创建者
和函数调用者
角度都能更好地与场景融合
可以使用下划线 _ 省略参数标签
func sum(_ v1: Int, _ v2: Int) ->Int {
v1 + v2
}
sum(10, 20)
调用时没有参数标签
一般不建议所有方法都省略掉,比较复杂的方法还是建议写清楚来更好
6. 默认参数值(Default Parameter Value)
参数可以有默认值
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
C++的默认参数值有个限制:必须从右往左设置。由于Swift拥有参数标签,因此并没有此类限制
但是在省略参数标签时,需要特别注意,避免出错
// 这里的middle不可以省略参数标签
func test(_ first: Int = 10, middle: Int, _ last: Int = 30) {}
test(middle: 20)
7. 可变参数(Variadic Parameter)
func sum(_ numbers: Int...) -> Int {
var total = 0
for number in numbers {
total += number
}
return total
}
sum(10, 20, 30, 40) // 100
一个函数
最多只能有1个
可变参数
紧跟
在可变参数后面
的参数不能省略
参数标签
// 参数string不能省略标签
func test(_ number: Int..., string: String, _ other: String) {}
test(10, 20, 30, string: "Jack", "Rose")
8. Swift自带的print函数
_ items: Any...
可变参数
separator: String = " "
,表示打印每个参数之间
的字符串。默认是空格" "
terminator: String = "\n"
,打印完所有参数后,要打印的字符串。默认是换行"\n"
9. 输入输出参数(In-Out Parameter)
可以用inout定义一个输入输出参数:可以在函数内部修改外部实参的值
常规情况下,我们无法修改外部实参的值
使用inout:
var number = 10
func test(_ num: inout Int) {
num = 20
}
test(&number)
外部实参的值已经修改
交换两个数的实现
func
let tmp = v1
v1 = v2
v2 = tmp
}
var num1 = 10
var num2 = 20
swapValues(&num1, &num2)
print(num1)
print(num2)
已交换:
可以使用元组
来实现交换
func swapValues(_ v1: inout Int, _ v2: inout Int) {
(v1, v2) = (v2, v1)
}
可变参数不能标记为inout
inout参数不能有默认值
inout参数只能传入可以被多次赋值的
inout参数的本质是地址传递(引用传递)
10. 函数重载(Function Overload)
10.1 规则
- 函数名相同
- 参数个数不同
||
参数类型不同||
参数标签不同
有如下函数:
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
}
以上调用结果:
10.2 函数重载注意点
10.2.1 返回值
类型与函数重载无关
// 返回值类型与函数重载无关
func sum(v1: Int, v2: Int) -> Int { v1 + v2 }
func sum(v1: Int, v2: Int) {}
sum(v1: 10, v2: 20)
10.2.2 默认参数值
和函数重载
一起使用产生二义性
时,编译器并不会报错
(在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) // 会调用 func sum(v1: Int, v2: Int)
10.2.3 可变参数
、省略参数标签
、函数重载
一起使用产生二义性
时,编译器有可能会报错
func sum(v1: Int, v2: Int) -> Int {
v1 + v2
}
func sum(_ v1: Int, _ v2: Int) -> Int {
v1 + v2
}
func sum(_ numbers: Int...) -> Int {
var total = 0
for number in numbers {
total += number
}
return total
}
sum(10, 20)
报错了:
如果将第一个函数注释掉,是正常的
11. 内联函数
11.1 如果开启了编译器优化(Release模式默认会开启优化),编译器会自动将某些函数变成内联函数
- 将函数调用展开成函数体
假设有这样一个函数:
func test() {
print("test")
}
test()
变成内联函数后:
print("test")
变成内联函数的目的:
减少调用开销。如上例子,就省去了调用函数时的
开辟栈空间
和回收栈空间
Debug
模式下默认不会开启优化
11.2 哪些函数不会被自动内联?
- 函数体比较长
- 包含递归调用
- 包含动态派发
函数体比较长
比如:
func test() {
print("test")
print("test")
print("test")
print("test")
print("test")
print("test")
}
假设调用两次函数:
test()
test()
如果还转成内联函数
print("test")
print("test")
print("test")
print("test")
print("test")
print("test")
print("test")
print("test")
print("test")
print("test")
print("test")
print("test")
最终会导致汇编代码特别多,安装包体积变大.
所以这种情况编译器不会
转成内联函数
包含递归调用
func test() {
test()
}
test()
包含动态派发
// 动态派发
class Person {
func test() {
}
}
class Student : Person {
override func test() {
}
}
class Teacher : Person {
override func test() {
}
}
var p: Person = Student()
p = Teacher()
p.test()
动态拍发,就是在运行时再决定要调用的代码
如上代码,变量p
指向的对象是随时可能变化的,所以编译器在编译的时候没办法确定调用的是Student
还是Teacher
。因此没法转成内联函数
12. @inline
做个简单了解
// 永远不会被内联(即使开启了编译器优化)
@inline(never) func test() {
print("test")
}
// 开启编译器优化后,即使代码很长,也会被内联(递归调用函数、动态派发的函数除外)
@inline(__always) func test() {
print("test")
}
在Release模式下,模拟器已经开启优化,会自动决定那些函数需要内联,因此没必要使用
@inline
13. 函数类型(Function Type)
每一个函数都是有类型的,函数类型由形式参数类型、返回值类型组成
将函数
赋值给变量
,并通过变量
调用函数
14. 函数类型作为函数参数
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) {
print("Result:\(mathFn(a, b))")
}
printResult(sum, 5, 2) // Result:7
printResult(difference, 5, 2) // Result:3
15. 函数类型作为函数返回值
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
返回值是函数类型的函数,叫做
高阶函数
(Higher-Order Function)如上
forward
就是高阶函数
16. typealias
typealias用来给类型起别名
typealias Byte = Int8
typealias Short = Int16
typealias Long = Int64
Swift种没有这三种数据类型,这么些相当于自定义了这三种类型
元组
类型的别名
typealias Date = (year: Int, month: Int, day: Int)
func test(_ date: Date) {
print(date.0)
print(date.year)
}
test((2011, 9, 10))
// 2011
// 2011
函数
类型的别名
typealias IntFn = (Int, Int) -> Int
func difference(v1: Int, v2: Int) -> Int {
v1 - v2
}
let fn: IntFn = difference
fn(20, 10) // 10
func setFn(_ fn: IntFn) {}
setFn(difference)
func getFn() -> IntFn { difference }
按照Swift标准库的定义,Void就是空元组()
17. 嵌套函数
将函数定义在函数内部
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
@oubijiexi