Swift函数类型和闭包
函数类型
函数类型定义
函数类型形式:(参数类型1,参数类型2,…) -> 返回值类型
函数类型是一种引用类型,类似于函数指针。可以将函数类型应用于任何使用类型的地方:变量、参数、返回值。
函数类型实例化支持:全局函数、嵌套函数、成员函数(实例方法和静态方法)
函数的内存模型
示例说明:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46func add(a:Double,b:Double) ->Double{
return a+b
}
func devide(a:Double, b:Double) ->Double{
return a/b
}
func multiply(a:Double,b:Double)->Double{
return a*b
}
func minus(a:Double,b:Double)->Double{
return a-b
}
class Matrix {
var row = 0.0
var column = 0.0
init(row:Double, column:Double) {
self.row = row
self.column = column
}
func process(x:Double, y:Double) -> Double {
return x * row + y * column
}
static func invoke(x:Double, y:Double) ->Double{
return x * x + y * y
}
}
var compute:(Double,Double) -> Double
compute = add//指向全局函数
let result1 = compute(10,20)
print(result1)
compute = devide//指向全局函数
let result2 = compute(10,20)
print(result2)
var matrix = Matrix(row: 10, column: 20)
compute = matrix.process//指向实例方法
compute(10,20)
compute = Matrix.invoke//指向静态方法
compute(30,40)compute
的内存模型在上面的例子中,compute是一个引用类型,在栈上有一个指针,指向堆上的一个对象。堆上的对象包含两个部分,一个是对象指针,一个函数指针。
如果
compute
是一个全局函数(add
或devide
),那么它的对象指针就是空的,函数指针就是这个函数(add或devide)的地址(任何一个函数编译之后加载到内存中后,都有一个函数的入口点地址);compute = add
就是把add
函数的入口点地址赋值给compute
的函数指针;compute = matrix.process
,对象指针指向matrix
,函数指针指向process
的入口点地址,process函数有一个隐含参数self,其实就是把对象指针指向了self。只有当
compute
指向实例方法的时候,对象指针才会指向实例;当compute
指向全局函数、嵌套函数、静态方法的时候,对象指针都是空的。当
compute
指向matrix.process
时,可以用伪码理解为:1
2
3
4compute = matrix.process//实例方法
// compute.object = matrix
// compute.method = &process
compute(10,20)//JMP compute.method闭包的理解
闭包是函数类型的实例,是一段自包含的代码块,可被用于函数类型的变量、参数或返回值。
三种闭包形式:
- 全局函数:具名函数,但不捕获任何值
- 嵌套函数:在函数内部嵌套定义具名函数,可以捕获包含函数中的值
- 闭包表达式:匿名函数类型实例,不具名的代码块,可捕获上下文的值
闭包是引用类型,闭包变量拷贝具有引用语义。
闭包和函数类型实例拥有同样的内存模型。
闭包表达式定义:
1
2
3{(参数类型1,参数类型2,...) -> 返回值类型 in
语句块
}闭包表达式的几种简化缩写形式:
- 自动类型推断:省略参数类型和返回值类型
- 单表达式闭包可以省略return关键词
- 盛勇参数缩略形式$0,$1…省略参数声明和in
- 将操作符函数自动推导为函数类型
- 尾随闭包:当闭包表达式为函数最后一个参数时,可将其写在括号后
- 自动闭包:不接受任何参数,直接返回表达式的值,允许延迟计算
函数类型与闭包的变量捕获
函数类型和闭包可以捕获其所在上下文的任何值:
- 函数参数
- 局部变量
- 对象实例属性
- 全局变量
- 类的类型属性
如果捕获值(参数和局部变量)生存周期小于闭包对象,系统会将被捕获的值封装在一个临时对象里,然后在闭包对象上创建一个对象指针,指向该临时对象。临时对象和闭包对象之间是强引用关系,生存周期跟随闭包对象。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18//捕获参数或局部变量
func addHandler(step:Int)->()->Int{
var sum = 15
return {
sum += step
return sum
}
}
let addByTen = addHandler(step: 10)
addByTen()//25
addByTen()//35
addByTen()//45
let addBySix = addHandler(step: 6)
addBySix()//21
addBySix()//27
addBySix()//33在上面的实例中,闭包捕获了sum和step,系统会将sum和step封装到一个对象中,上面的代码可以理解成下面的代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22//生成一个类来保存sum和step
class AddHelper{
var sum = 0
var step = 0
func add() -> Int {
sum += step
return sum
}
}
func addHandlerComplete(step:Int)->()->Int{
let sum = 0
let obj = AddHelper()
obj.sum = sum
obj.step = step
return obj.add
}
let addByTen = addHandlerComplete(step: 10)//闭包的对象指针指向obj,函数指针指向add函数的入口地址
addByTen()//每次调用addByTen()时,obj不变,add改变了obj的sum
addByTen()
addByTen()addByTen这个闭包的内存模型: