函数分类
- 无参无返
下面三种方式都可以
Void在swift里的定义就是空元组()
func say() -> Void {
print("hello")
}
func say() -> () {
print("hello")
}
func say() {
print("hello")
}
- 无参有返
func pi() -> Double {
return 3.14
}
- 有参无返
func say(text:String) {
print(text)
}
- 有参有返
函数的形参是let,也只能是let
func add(a:Int, b:Int) -> Int {
return a + b
}
隐式返回
如果整个函数体是一个单一表达式,那么函数会隐式返回这个表达式
func add(a:Int, b:Int) -> Int {
a + b // 可以省略return
}
let sum = add(a: 10, b: 20)
print(sum) // 打印: 30
函数的文档注释
注释及其效果.png参数标签
可以修改参数标签,这样的目的是为了让函数调用的时候,更像是一个完整的英文句子,更好理解函数的意思。
func goToWork(at time:String) {
print("time is \(time)")
}
goToWork(at: "8:00") // 很像 go to work at 8:00
可以使用下划线_省略参数标签,不过不建议这么使用
func add(_ a:Int, _ b:Int) -> Int {
return a+b
}
add(10, 20)
默认参数值
可以在声明方法的时候,为参数设置一个默认值,当方法调用时没传入该参数时,该参数值为默认值。
func check(name: String = "Jonas", age: Int, job: String = "none") {
print("name=\(name),age=\(age),job=\(job)")
}
check(age: 15) // name=Jonas,age=15,job=none
check(name: "Bob", age: 20, job: "Doctor") // name=Bob,age=20,job=Doctor
check(name: "Mary", age: 18) // name=Mary,age=18,job=none
check(age: 30, job: "postman") // name=Jonas,age=30,job=postman
可变参数
在参数类型后面加...表示可以传入不等数量的参数,相当于是一个数组
- 一个函数最多只能有一个可变参数
func countNumber(numbers: Int...) {
print(numbers) // [1,2,3,4,5]
}
countNumber(numbers: 1,2,3,4,5)
- 紧跟在可变参数后面的参数不能省略参数标签
下面的例子在可变参数numbers后面的是student参数,它不能使用下划线来省略参数标签。
func countNumber(numbers: Int..., student: String, _ age: Int) {
print(numbers) // [1,2,3,4,5]
}
countNumber(numbers: 1,2,3,4,5, student: "Bob", 15)
输入输出参数(In-Out-Parameter)
- 可以用inout定义一个输入输出参数:可以在函数内部修改外部实参的值
- 输入输出参数在传参时要加 &
- 可变参数不能标记为输入输出参数
- inout参数不能有默认值
- inout参数的本质是地址传递(引用传递)
-
inout参数只能传入可以被多次赋值的
let修饰的常量就不可以,或者直接传一个字面量也不可以。
func change(num: inout Int) {
num = 20
}
var number = 10
change(num: &number)
print(number) // 20
函数重载
重载的函数可以和原函数共存,虽然有多个名字一样的函数,但是也可以根据情况不同调用不同的函数。
-
规则
1、函数名相同
2、参数个数不同 || 参数类型不同 || 参数标签不同
-
返回值类型不构成重载
下图所示两个方法只有返回值类型不同,但是不构成重载,调用会报错。
- 默认参数值和函数重载一起使用产生二义性时,编译器并不会报错(在C++中会报错)
- 可变参数、省略参数标签、函数重载一起使用产生义性时编译器有可能会报错
内联函数
-
如果开启了编译器优化( Release模式默认会开启优化) , 编译器会自动将某些函数变成内联函数
将函数调用展开成函数体
-
哪些函数不会被内联?
1、函数体比较长:函数体太长如果内联,多次调用会增大汇编代码数量
2、包含递归调用:会导致死循环
3、包含动态派发:在编译时无法确定调用方法
@inline
// 永远不会内联,即使开启了编译器优化
@inline(never) func test() {
print("hello")
}
// 开启编译器优化后,即使函数内的代码很多,也会被内联(递归调用函数,动态派发的函数除外)
@inline(__always) func test() {
// ...很多的代码
}
release模式下,编译器开启优化,会自动决定内联函数,所以这个@inline也基本不用。
函数类型
- 每一个函数都是有类型的,函数类型由形式参数类型、返回值类型组成
func method() {} // 函数类型:() -> Void 或者() -> ()
func add(a: Int, b: Int) -> Int {
return a + b
} // 函数类型:(Int, Int) -> Int
- 使用函数类型,可以定义变量
func method() {} // 函数类型:() -> Void 或者() -> ()
func add(a: Int, b: Int) -> Int {
return a + b
} // 函数类型:(Int, Int) -> Int
var fn : (Int, Int) -> Int = add
fn(10, 20) // 30 调用时不需要参数标签
- 使用函数类型,可以作为参数类型
func add(a: Int, b: Int) -> Int {
return a + b
} // 函数类型:(Int, Int) -> Int
func printResult(mathFn: (Int, Int) -> Int, a: Int, b: Int) {
let result = mathFn(a, b)
print(result)
}
printResult(mathFn: add, a: 20, b: 30) // 50
-
函数类型作为函数返回值
返回值是函数类型的函数,叫做高阶函数
func add(num: Int) -> Int {
return num + 1
}
func reduce(num: Int) -> Int {
return num - 1
}
func select(result: Bool) -> (Int) -> Int {
return result ? add : reduce
}
// select返回的是add函数
select(result: true)(10) // add函数传入10结果为11
typealias
typealias用来给类型起别名
// 为一个元组起别名为DateType
typealias DateType = (year: Int, month: Int, day: Int)
func method(date: DateType) {
print("今天是\(date.year)年\(date.month)月\(date.day)日")
}
method(date: (2022, 3, 10)) // 今天是2022年3月10日
// 为一个函数类型起别名为fnType
typealias fnType = (Int, Int) -> Int
func add(a: Int, b: Int) -> Int {
a + b
}
let method : fnType = add
method(10, 20) // 30
嵌套函数
-
将函数定义在函数内部
嵌套函数可以将内部的函数隐藏起来,不让外部直接调用
func selectMethod(result: Bool) -> (Int, Int) -> Int {
func add(a: Int, b: Int) -> Int {
a + b
}
func reduce(a: Int, b: Int) -> Int {
a - b
}
return result ? add : reduce
}
let fn = selectMethod(result: true) // add
fn(10, 20) // 30
网友评论