前言
-
Swift 中函数是用来完成特定任务的独立代码块。
- 本节所讲的函数概念是全局的,不隶属于某一个数据结构。
- 没有 “拥有者” 的函数的作用域是全局的,除非重载,否则不可能定义两个一模一样的函数。
- 在 Swift 语言中没有主函数。
-
方法是拥有归属关系的函数。
- 方法的 “拥有者” 可能是类,也可能是结构体等。
- 方法的 “拥有者” 为方法提供了命名空间,所以在不同的类或者结构体中可以定义完全相同的方法。
1、函数的定义
-
函数定义需要关键字
func
,其一般格式为func 函数名 (参数名1: 参数类型, 参数名2: 参数类型, ...) -> 函数返回类型 { 函数体 ..... return 返回值 }
-
参数
- 在函数定义中,我们使用的参数叫形式参数,在调用时传入的参数是实际参数。
- 参数名后面用冒号相连的是参数类型,在定义函数时,需要明确参数类型。
- 你可以指定多个参数,参数间用
,
分割。
-
返回类型
- 返回值类型使用组合符号
->
来指示。 - 如果不需要返回值可以写成
-> Void
(注意 V 要大写),或者直接省略->
语句。 - 没有定义返回类型的函数默认会返回
Void
。 - 如果
->
后面是Void
,则函数体中不需要写return
语句。
- 返回值类型使用组合符号
-
函数调用
- 函数调用时只要传入的参数与函数定义中的参数类型相同即可。
- 在 Swift 3.0 之前调用函数时,函数的首个参数名是自动隐藏的。
- 在 Swift 3.0 及之后调用函数时,函数的所有参数是同等地位的,首个参数不再默认隐藏。
2、函数类型
-
Swift 中函数本身也是一种类型,函数类型通常由函数的形参类型和返回值类型组成。
-
例如,函数
func addString(s1: string, s2: string, s2: string) -> String { }
-
的 函数类型 是
(String, String, String) -> String
-
-
如果一个函数没有形参或返回值,那么这个函数的类型是
() -> ()
。
-
-
可以像使用 Swift 语言中其他类型一样使用函数类型。
-
例如可以定义一个函数常量或函数变量,并像一般数据类型指定初始值一样为他指定一个对应的函数。与其他类型一样,当你给函数赋一个变量或者常量时,你可以让 Swift 语言去推断函数的类型。
func addString(s1: string, s2: string, s2: string) -> String { } // 指定函数类型 var addSome:(String, String, String) -> String = addString // 推断函数类型 let addSome = addString
-
你也可以将函数作为参数传递给其他函数,或者将函数类型当作返回类型。
func plus(a: Int, b: Int) -> Int { return a + b } func mult(a: Int, b: Int) -> Int { return a * b } func someFunction(func: (Int, Int) -> Int, a: Int, b: Int) { let result = paraFunc(a, b) print("The result is (result)") }
someFunction(paraFunc: plus, a: 3, b: 4) // The result is 7 someFunction(paraFunc: mult, a: 3, b: 4) // The result is 12
-
3、函数参数
- 在 Swift 语言中,函数的形参和返回值是非常具有灵活性的,在需要的时候,可以定义一个或者多个甚至选择性的省略。
3.1 外部形参
-
Swift 语言也能支持 OC 的函数参数标签模式,这种模式被称为外部形参。
- 如果你为参数制定了外部形参名,那么在调用的时候就必须显式的使用。
- 如果别人第一次阅读你的代码,使用外部形参名称可以使你要表达的意思更加明确,上下文更加清晰。
- 在函数调用时,如果你不想显示形参名,可以使用下划线
_
作为外部形参名。
-
定义格式
// 使用时本地形参名(本地形参名1、本地形参名2, ...)会被省略 func 函数名 (外部形参名1 本地形参名1: 参数类型, 外部形参名2 本地形参名2: 参数类型, ...) -> 函数返回类型 { 函数体 ..... return 返回值 }
-
在写外部形参名时,完全可以只写一次名字,只需要用一个 hash 符号
#
作为参数名称的前缀,从而告诉 Swift 我们使用了名称相同的本地形参名称和外部形参名称。 -
定义格式
func 函数名 (#参数名1: 参数类型, #参数名2: 参数类型, ...) -> 函数返回类型 { 函数体 ..... return 返回值 }
- 从 Swift 3.0 起不再支持该种定义方式。
3.2 默认值形参
-
在 Swift 语言中可以为任何形参定义默认值以作为函数定义的一部分。
- 如果已经定义了默认值,那么调用函数时就可以省略该形参。
- 为了避免遗漏参数或者参数传递的二义性,需在函数形参列表的末尾放置带默认值的形参,不要在非默认值的形参前放置。
- 在有定义默认值的情况下,当没有指定外部形参名称时,Swift 语言将为你定义的任何默认值形参提供一个自动外部形参名,这个自动外部形参名和本地形参名相同。
-
定义格式
func 函数名 (参数名1: 参数类型, 参数名2: 参数类型, 默认值形参名: 参数类型 = 默认值) -> 函数返回类型 { 函数体 ..... return 返回值 }
3.3 可变数量形参
-
当不能确定调用函数需要传入具体参数的数量时,应使用可变形参。
-
可变形参是指可接受零个或多个指定类型值的形参,可以用它来传递任意数量的输入参数。
- 声明可变形参时需在参数类型后面使用
...
。 - 当参数传递进函数体后,参数在函数体内可以通过集合的形式访问。
- 一个函数最多可以有一个可变参数,而且它必须出现在参数列表的最后。
- 声明可变形参时需在参数类型后面使用
-
定义格式
func 函数名 (参数名1: 参数类型, 参数名2: 参数类型, 可变形参名: 参数类型...) -> 函数返回类型 { 函数体 ..... return 返回值 }
-
可变数量形参的使用
func arithmeticmean(numbers: Double...) -> Double { var total: Double = 0 for number in numbers { total += number } return total / Double(numbers.count) }
print(arithmeticmean(numbers: 1, 2, 3)) // 2.0 print(arithmeticmean(numbers: 1, 2, 3, 4, 5)) // 3.0
3.4 可变值形参
-
Swift 语言函数的形参默认是常量,我们不能直接在函数体内部改变形参的值,也就是说函数的形参默认是值类型的。
-
但是如果需要在函数体内部修改函数参数值,可以使用可变参数,
- 要定义可变参数可以在参数名前使用 var 关键字。
- 可变参数可以让你能够修改形参的值,它可以给函数体一个可修改的形参值副本。
- 但这并不意味着可变形参就是引用类型的。
-
虽然在 Swift 2.2 中仍旧可以使用这个语法,但是在 Swift 3.0 中这种写法已经被废弃了,如果需要修改参数的值,可以在函数体内创建新的成员变量。
-
定义格式
func 函数名 (var 参数名1: 参数类型, var 参数名2: 参数类型, ...) -> 函数返回类型 { 函数体 ..... return 返回值 }
3.5 引用形参
-
在实际的编码中,我们往往需要在函数体内部修改形参值,并同时作用到实参本身,从而省去增加返回值数量的步骤。
- 这时可以把形参定义为 in-out(输入输出参数)类型。
- Swift 3.0 之前,要定义 in-out 类型的参数,需要在参数名前使用
inout
关键字修饰。 - Swift 3.0 及之后,要定义 in-out 类型的参数,需要在参数类型前使用
inout
关键字修饰。 - 当把变量传递给 in-out 形参时,必须在变量前添加 “&” 符号,以表明他被函数内部修改的是它本身的引用。
-
定义格式
// Swift 3.0 之前 func 函数名 (inout 参数名1: 参数类型, 参数名2: 参数类型, ...) { 函数体 ..... }
// Swift 3.0 及之后 func 函数名 (参数名1: inout 参数类型, 参数名2: 参数类型, ...) { 函数体 ..... }
-
使用 in-out 参数的同时有几条规则需要注意
- 只能输入一个变量作为输入输出参数,不能将常量和字面量传递进函数。
- 不能同时将参数标记为
var
、let
、inout
。 - 可变形参的参数不能标记为
inout
。 - 函数不能有默认值。
-
引用形参的使用
func exampleOfFunction(parameter: inout String) -> String { parameter = "Hello (parameter)" return parameter }
var name = "xiaoming" print(exampleOfFunction(parameter: &name)) // Hello xiaoming print(name) // Hello xiaoming
4、嵌套函数
-
在一个函数体中定义另外一个函数体就称为嵌套函数。
- 嵌套的函数默认对外是隐藏的,但仍可以通过包裹他们的函数调用和使用它们。
-
定义格式
func 函数名1 (参数名1: 参数类型, 参数名2: 参数类型, ...) -> 函数返回类型1 { func 函数名2 (参数名3: 参数类型, 参数名4: 参数类型, ...) -> 函数返回类型2 { 函数体2 ..... return 返回值2 } 函数体1 ..... return 返回值1 }
5、函数重载
-
在 Swift 中函数重载十分常用。
- 由于 Swift 是一门强类型语言,可以修改参数列表或者使用不同的返回值实现函数重载。
- 另外 Swift 中有一些特殊的函数是以操作符的形式定义的,比如
==
,如果想让自己定义的数据结构可以使用某个操作符,那么可以对操作符进行重载。
-
参数列表重载的使用
func test(s: Int) -> Int { print("Int") return 1 } // 修改参数名 func test(a: Int) -> Int { print("String") return 2 } // 修改参数类型 func test(s: String) -> Int { print("String") return 3 }
let intValue = test(s: 0) // 1 let intValue = test(a: 0) // 2 let stringValue = test(s: "") // 3
- 在调用函数时,编译器会检查函数所处的 “上下文”,确定参数名和参数的类型,调用不同的实现。
-
返回值重载的使用
func test(s: String) -> Int { print("Int") return 3 } // 修改返回值类型 func test(s: String) -> String { print("String") return s }
let intValue : Int = test(s: "") // 3 let stringValue: String = test(s: "") // ""
-
在调用函数时,编译器会检查函数所处的 “上下文”,确定返回值的类型,调用不同的实现。
-
因为函数存在多种 “上下文”,因此类型推断不再适用了。
-
如果写出下面的代码编译器会提示你明确 unKnow 的类型。
let unKnow = test(s: "")
-
-
操作符重载的使用
struct BoxInt { var intValue: Int } func == (lhs: BoxInt, rhs: BoxInt) -> Bool { return lhs.intValue == rhs.intValue }
let b1 = BoxInt(intValue: 1) let b2 = BoxInt(intValue: 2) print(b1 == b2) // false
5、函数风格
-
Swift 3.0 之后,函数风格发生了较大的变化,这主要是受 Swift 3.0 提出的 API 设计原则的影响。
-
1)缩减函数名
-
在 Swift 3.0 之前,Swift 的函数风格与 OC 类似,函数名需要承担描述首个参数的职责,在函数调用时,函数中首个参数的参数名默认会被隐藏,只在函数体中使用。
-
从 Swift 3.0 开始,函数的首个参数名不再默认被隐藏,你可以把函数的方法名缩减,把关键字转移到首个参数的参数名上。
// Swift 3.0 之前 arr.appendContentsof([4, 5]) // Swift 3.0 及之后 arr.append(contentsOf: [4, 5])
- 这样无论是添加单个元素,还是添加整个数组的方法都统一为 append,API 更加整齐。
-
-
2)用 “时态” 表示函数 “副作用”
-
在通常的认知中,函数应该是 “无状态” 的,也就是说,无论调用多少次都能得到相同的结果,但是,有时候函数体内部也会修改函数外部的成员值。
var mutableArr = [1, 3, 2] // 在 Swift 3.0 之前,sort 方法叫做 sortInPlace mutableArr.sort() // sorted 是 sort 的副作用版本 let immutableArr = mutableArr.sorted()
sort
是动词时态,代表这个方法没有副作用,也就是说,sort
方法只会对方法的调用者起作用,是一种原地排序。sorted
是sort
方法的副作用版本,sorted
方法不会修改原数组,会把原数组拷贝后排序,并且返回排序好的数组,你可以把返回值理解成一种副作用。
-
-
3)关键字可以用作参数名
-
从 Swift 3.0 开始,大多数 Swift 关键字都可以被用作参数名。
func index(for element: Int, in collection: [Int]) -> Int? { for (num, e) in collection.enumerated() { if e == element { return num } } return nil }
index(for: 3, in: [1, 2, 2, 2, 3]) // 4
for-in
关键字原本是用在循环中,在这里被用作函数的外部参数名。
-