Swift之闭包
在Swift开发文档中是这样介绍闭包的:闭包是可以在你的代码中被传递和引用的功能性独立模块。
闭包的形式有:
全局函数 | 嵌套函数 | 闭包表达式 |
---|---|---|
有名字但不能捕获任何值。 | 有名字,也能捕获封闭函数内的值。 | 无名闭包,使用轻量级语法,可以根据上下文环境捕获值。 |
Swift中的闭包有很多优化的地方:
- 根据上下文推断参数和返回值类型
- 从单行表达式闭包中隐式返回(也就是闭包体只有一行代码,可以省略return)
- 可以使用简化参数名,如$0, $1(从0开始,表示第i个参数…)
- 提供了尾随闭包语法(Trailing closure syntax)
创建基本的闭包
let driving = {
print("我要去开车")
}
上面的代码实际上创建了一个匿名的函数,并将这个函数赋给了 driving。之后你就可以把 driving() 当作一个常规的函数来用,就像这样:
driving()
在闭包中接收参数
当你创建闭包的时候,它们并没有名字,也没有提供书写参数的地方。但这并不意味着它们不能接收参数,只不过它们接收参数的方式稍有不同:这些参数是被写在花括号
里面的。
为了让一个闭包接收参数,你需要在花括号之后把这些参数列出来,然后跟上一个in
关键字。这样就告诉Swift,闭包的主体是从哪里开始的。
举个例子,我们来创建一个闭包,接收一个叫 place 的字符串作为唯一的参数,就像这样:
let driving = { (place: String) in
print("我要开车去 \(place)。")
}
函数和闭包的一个区别是运行闭包的时候你不会用到参数标签。因此,调用 driving() 的时候,我们是这样写的:
driving("北京")
从闭包中返回值
闭包也能返回值,写法和闭包的参数类似:写在闭包内部, in 关键字前面。
还是以 driving() 闭包为例, 让它返回一个字符串。原来的函数是这样的:
let driving = { (place: String) in
print("我要开车去 \(place)。")
}
改成返回字符串而不是直接打印那个字符串,需要 in
之前添加 -> String,然后像常规函数那样用到 return 关键字:
let drivingWithReturn = { (place: String) -> String in
return "我要开车去 \(place)。"
}
现在我们运行这个闭包并且打印出它的返回值:
let message = drivingWithReturn("北京")
print(message)
闭包作为参数
既然闭包可以像字符串和整数一样使用,你就可以将它们传入函数。闭包作为参数的语法乍一看一看挺伤脑筋的,让我们慢慢来。
首先,还是基本的 driving() 闭包。
let driving = {
print("我正在开车。")
}
如果我们打算把这个闭包传入一个函数,以便函数内部可以运行这个闭包。我们需要把函数的参数类型指定为 () -> Void。 它的意思是“不接收参数,并且返回 Void”。在Swift中,Void是什么也没有的意思。
好了,让我们来写一个 travel() 函数,接收不同类型的 traveling 动作, 并且在动作前后分别打印信息:
func travel(action: () -> Void) {
print("我准备出发了。")
action()
print("我到达目的地了。")
}
现在可以用上 driving 闭包了,就像这样:
travel(action: driving)
拖尾闭包语法
如果一个函数的最后一个参数是闭包,Swift允许你采用一种被称为 “拖尾闭包语法” 的方式来调用这个闭包。你可以把闭包传入函数之后的花括号里,而不必像传入参数那样。
又用到我们的 travel() 函数了。它接收一个 action 闭包。闭包在两个 print() 调用之间执行:
func travel(action: () -> Void) {
print("我准备出发了。")
action()
print("我到达目的地了。")
}
由于函数的最后一个参数是闭包,我们可以用拖尾闭包语法来调用 travel() 函数,就像这样:
travel() {
print("我正在开车。")
}
实际上,由于函数没有别的参数了,我们还可以将圆括号完全移除:
travel {
print("我正在开车。")
}
拖尾闭包语法在Swift中非常常见,所以你要适应它。