第七章 闭包
1. Closure Expression (闭包的表达)
闭包是一个自包含功能型的代码块,它本身可以被我们的代码所传递和使用。闭包可以获取和存储它所在上下问文中常量或变量的对应引用。这个被称之为包的常量或变量,Swift会为我们处理获取和存储过程中的内存管理。一般在写闭包的时候通常会用在类似函数的结构体内以简明方便的方法建一个内联闭包。
全局函数和嵌套函数是一个特殊的闭包类型。闭包有以下三种形式
- 全局函数是一个有名字但不会捕获任何值的闭包
- 嵌套函数是一个有名字并可以捕获其封闭函数域内值的闭包
- 闭包表达式是一个利用轻量级语法所写的可以捕获其上下文中变量或常量值的匿名闭包
Swift的闭包表达式用简洁的风格,并鼓励在常见场景中进行语法优化
优化主要包括了一下几点:
- 利用上下文推断参数和返回值的类型
- 隐式返回单表达式闭包,即单表达式闭包可以省略 return 关键字
- 参数名称缩写
- 尾随闭包语法
1.1 The sorted method (sorted(by:)方法在闭包中的使用)
swift标准库提供了sorted方法,它将会把数组里面的值按照一定的顺序排列分类,函数的参数必须为一致的。就像下面这个例子那样都是参数为String的情况。
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
func backward(_ s1: String, _ s2: String) -> Bool {
return s1 > s2
}
var reversedNames = names.sorted(by:backward)
// reversedNames现在变成了["Ewa", "Daniella", "Chris", "Barry", "Alex"]
这个和sorting排序算法是一样的 把数组里面的值 和前一个值做比较 第一个值大于第二个值 第二个值大于第三个值… 把所有值按照谁前一个值比后一个值大的顺序排列起来。
1.2 Closure expression syntax (闭包的表达语法)
可以把闭包的表达与法带入上面的例子中,闭包中的参数必须为相同类型且这个参数是不能有默认值。参数在这个闭包的表达式里面可以是输入输出型 (
in-out
) 的参数,可变参数也可以被用做闭包的参数类型,元组也可以被用做闭包的参数和返回值。下面这个例子就是用闭包表达式的方法写backward()函数的。
reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in
return s1 > s2
})
闭包的函数体是由这个
in
关键字引出的,并且in关键字表示闭包的参数和返回值都已经定义完成,闭包的函数体即将开始,由上面的例子我们可以得知 闭包的参数是
(s1: String, s2: String)
和 返回值
-> Bool
闭包的函数体比较少可以吧整个表达式写在同一行,不用在分多行来写这个表达式。用一行写整个闭包的表达式确实有点长。
1.3 Inferring type from context (从上下文来推断闭包参数及返回值类型)
sorted闭包函数是由sorted()方法的参数传入的,因为sorted()方法是被字符串调用的,swift可以推断它的参数和返回值的类型。sorted闭包函数即排序swift自然能推断出它的类型是
string
,而排序就回涉及到先后或大小返回值也就能被推断出是
bool
了 所以在闭包表达中参数类型string和返回值类型bool包括->都可以省略不写。
// 省略参数和返回值类型的闭包表达式
reversedNames = names.sorted(by: { s1, s2 in return s1 > s2 } )
1.4 Implicit return from single-expression closure (表达式中的隐式返回)
单一的闭包表达式可以省略return关键字 所以在一行的闭包表达式里面可以隐藏参数的类型返回值类型 和return关键字。
// 省略关键字return的表达式
reversedNames = names.sorted(by: { s1, s2 in s1 > s2 } )
1.5 Shorthand argument names (缩写实参数名称)
swift自动提供了内联闭包的缩写方式 我们直接可以用$0,$1,$2 … 如果我们用了这些参数的缩写 那么我们就可以从定义里面省略闭包的参数列表。这里的$0,$1,$2 … 指代的是参数中的第一个第二个第三个 … 的String参数
1.6 Operator method (运算符方法)
由一种更简短的方法来写上面的闭包表达式
2. Trailing Closure (尾随闭包)
如果你要将一个比较长的闭包表达式作为实参传递给函数的时候,我们就要在该函数块之后添加这个尾随闭包,用尾随闭包来增强该函数的可读性,函数会将尾随闭包作为最后一个参数进行调用,写尾随闭包的时候可以不用写该闭包的实参标签。下面的例子或有具体说明
func someFunctionThatTakesAClosure(closure: () -> Void) {
// 这里是函数体
}
somefunctionThatTakesAClosure(closure: {
// 这里是闭包体
})
// 调用尾随闭包
someFunctionThatTakesAClosure() {
// 这里是尾随闭包体
}
上一章那个String值的排序方法也可以被用尾随闭包的来表达,尾随闭包可以写在
sorted(by: )
方法的括号外面。
如果我们用闭包的表达式只被用做一个函数或方法的实参,并且我们提供的这个闭包是尾随闭包,那么我们可以在函数或方法的名字之后省略括号()
尾随闭包一般用在闭包表达式足够长的情况下,那么这个时候我们就不能在一行写这个内联闭包了,就像下面这个例子,Array类型由一个映射
map(_:)
方法。这个方法会把闭包表达式作为array的一个单一的实参。
下面就是一个如何用映射
map(_:)
方法把array里面的Int值转换成String值
// 创建一个字典
let digitNames = [
0: "Zero", 1: "One", 2: "Two", 3: "Three", 4: "Four",
5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine"
]
// 创建数字数组
let numbers = [16, 58, 510]
将尾随闭包通过传递给数组里面的映射
map(_:)
方法,现在我们可以用这个number值的array来创建一个string值的array。
let strings = numbers.map { (number) -> String in
var number = number
var output = ""
repeat {
// 这里存在一个问题
output = digitNames[number % 10]! + output
number /= 10
} while number > 0
return output
}