闭包
Swift支持闭包,相当于其他函数里面的匿名函数的意思。经常是在一些需要传递给其他函数的参数也是一个函数类型的时候,而这个需要被传递的函数又包含的代码是短小的。
创建闭包
闭包语法
形式就是{(parameter list)-> return type in function implements} in关键字用来分隔函数体与函数类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23// 用之前函数排序那个例子
func sortIntArray(inout array:[Int], compare:(Int, Int)->Bool)
{
for i in 0 ..< array.count - 1
{
for j in 0 ..< array.count - 1 - i
{
if compare(array[j], array[j + 1])
{
let tmp = array[j]
array[j] = array[j + 1]
array[j + 1] = tmp
}
}
}
}
var intArray = [1, 3, 10, -1, 100, 99]
// compare后的参数就是闭包的基本形式,这里不再像之前那样再写一个短小的比较函数
// 直接使用{}定义一个闭包就可以,效果与之前的lessFunc相同
sortIntArray(&intArray, compare: {(fir:Int, sec:Int)->Bool in return fir >= sec})
print(intArray)除了in和func关键字,还有函数名基本与定义一个函数类似。
闭包形式简化
已知的函数类型可以省略函数类型,因为Swift可以自动推断对应的函数类型,只需要知道对应的局部参数名称即可
1
2
3
4
5// 上例中的compare是明确知道函数类型是(Int, Int)->Bool类型的
// 上文中的闭可进一步简化成下面的形式
sortIntArray(&intArray, compare: {(fir, sec) in return fir >= sec})
// 甚至参数列表的小括号也可以不要
sortIntArray(&intArray, compare: {fir, sec in return fir >= sec})闭包还可以进一步简化,Swift可以隐式的推断单行表达式的返回结果,如上文中的compare参数,需要一个Bool的返回值,而闭包的函数体只有一个单行的表达式,所以这里可以继续把return关键字省略
1
2// 进一步省略如下
sortIntArray(&intArray, compare: {(fir, sec) in fir >= sec}) // 去掉return关键字还可以再次简化,Swift支持闭包中的参数缩写,即对应使用$ + 对应参数列表中的位置来代表对应的参数列表中的局部参数,比如上文中的fir和sec可以使用 $1和$2来代替,这样就可以把局部参数名称的声明也去掉了,没有函数类型,所以也就不需要in关键字了
1
2// 参数名称缩写,进一步简化
sortIntArray(&intArray, compare: ({$1 >= $2})
尾闭包
当闭包表达式是一个参数的最后一个参数时,可以将闭包的{}部分放在函数调用的函数列表之外,以此来增强函数体实现的可读性。
多个参数的尾闭包
1
2
3
4
5// 上文的例子就是一个尾闭包,闭包表达式是函数参数的最后一个参数
// 可以这样进行书写
sortIntArray(&intArray){(fir:Int, sec:Int)->Bool in return fir >= sec}
// 最后的简化形式,按尾闭包来写
sortIntArray(&intArray){$1 >= $2}只有一个参数的尾闭包,甚至还可以去函数调用的小括号去掉,直接接上闭包的{}
1
2// 如内置的sort函数
numbers.sort{n2 >= n2} // sort只接受一个函数类型
值捕获
闭包可以捕获其上下文中的变量/常量,即使出了对应的变量/常量已经出了对应的作用域,也能正常保持该值的引用和修改,类似像在一个函数返回另一个嵌套的函数,这个嵌套的函数就可以保持住在原函数中变量/常量的值,并且Swift会全权对闭包内存进行管理。
1 | func makeIncrementByNum(num:Int)->()->Int |
闭包是引用类型
闭包无论赋值给几个变量或者是常量,闭包内的变量/常量值都是对应函数的引用,不会随着赋值进行拷贝,所以无论你将同一个闭包赋值给几个变量/常量,还是原来的引用
1 | // 如上例中的makeInc1再次赋值给makeInc2 |
逃逸/非逃逸闭包
非逃逸闭包
非逃逸闭包的意思就是闭包传入一个函数中,函数执行完了,这个闭包也就不再起作用了,而逃逸闭包正好相反。Swift中的闭包默认是逃逸的,如果要使一个闭包是非逃逸闭包,在传入函数的参数前使用@noescape标签
1
2
3
4
5// 上文中sortArray,使compare是非逃逸闭包
func sortIntArray(inout array:[Int], @noescape compare:(Int, Int)->Bool)
{
//...
}逃逸闭包
使闭包逃逸出函数的执行体的办法是将闭包存到一个外部的集合里(如数组,集合),在函数返回的时候再调用对应的集合里的闭包代码,这时如果将对应函数参数标为@noescape的话就会被Swift检测到,会报错。
1
2
3
4
5
6
7
8
9
10
11
12// 保存要escape的闭包
var funcDict:[String:()->String] = [:]
func testEscape(f:()->String)
{
funcDict["testEscape"] = f
print("testEscape function execute completed")
}
// 先输出testEscape function execute completed
// 再输出closure in testEscape
testEscape{return "closure in testEscape"}
print(funcDict["testEscape"]!())