fix 2.7 closures(2)

This commit is contained in:
stanzhai
2014-06-14 17:23:00 +08:00
parent fee9162d6c
commit 6b9026dd9b

View File

@ -1,5 +1,4 @@
> 翻译wh1100717 > 翻译wh1100717
> 校对lyuka > 校对lyuka
# 闭包Closures # 闭包Closures
@ -19,7 +18,6 @@ Swift 中的闭包与 C 和 Objective-C 中的代码块blocks以及其他
这就是所谓的闭合并包裹着这些常量和变量俗称闭包。Swift 会为您管理在捕获过程中涉及到的所有内存操作。 这就是所谓的闭合并包裹着这些常量和变量俗称闭包。Swift 会为您管理在捕获过程中涉及到的所有内存操作。
> 注意: > 注意:
>
> 如果您不熟悉捕获capturing这个概念也不用担心您可以在 [值捕获](#capturing_values) 章节对其进行详细了解。 > 如果您不熟悉捕获capturing这个概念也不用担心您可以在 [值捕获](#capturing_values) 章节对其进行详细了解。
在[函数](../chapter2/06_Functions.html) 章节中介绍的全局和嵌套函数实际上也是特殊的闭包,闭包采取如下三种形式之一: 在[函数](../chapter2/06_Functions.html) 章节中介绍的全局和嵌套函数实际上也是特殊的闭包,闭包采取如下三种形式之一:
@ -54,7 +52,7 @@ Swift 标准库提供了`sort`函数,会根据您提供的基于输出类型
下面的闭包表达式示例使用`sort`函数对一个`String`类型的数组进行字母逆序排序,以下是初始数组值: 下面的闭包表达式示例使用`sort`函数对一个`String`类型的数组进行字母逆序排序,以下是初始数组值:
``` ```swift
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"] let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
``` ```
@ -67,7 +65,7 @@ let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
提供排序闭包函数的一种方式是撰写一个符合其类型要求的普通函数,并将其作为`sort`函数的第二个参数传入: 提供排序闭包函数的一种方式是撰写一个符合其类型要求的普通函数,并将其作为`sort`函数的第二个参数传入:
``` ```swift
func backwards(s1: String, s2: String) -> Bool { func backwards(s1: String, s2: String) -> Bool {
return s1 > s2 return s1 > s2
} }
@ -88,7 +86,7 @@ var reversed = sort(names, backwards)
闭包表达式语法有如下一般形式: 闭包表达式语法有如下一般形式:
``` ```swift
{ (parameters) -> returnType in { (parameters) -> returnType in
statements statements
} }
@ -100,7 +98,7 @@ var reversed = sort(names, backwards)
下面的例子展示了之前`backwards`函数对应的闭包表达式版本的代码: 下面的例子展示了之前`backwards`函数对应的闭包表达式版本的代码:
``` ```swift
reversed = sort(names, { (s1: String, s2: String) -> Bool in reversed = sort(names, { (s1: String, s2: String) -> Bool in
return s1 > s2 return s1 > s2
}) })
@ -115,7 +113,7 @@ reversed = sort(names, { (s1: String, s2: String) -> Bool in
因为这个闭包的函数体部分如此短以至于可以将其改写成一行代码: 因为这个闭包的函数体部分如此短以至于可以将其改写成一行代码:
``` ```swift
reversed = sort(names, { (s1: String, s2: String) -> Bool in return s1 > s2 } ) reversed = sort(names, { (s1: String, s2: String) -> Bool in return s1 > s2 } )
``` ```
@ -128,7 +126,7 @@ reversed = sort(names, { (s1: String, s2: String) -> Bool in return s1 > s2 } )
`sort`期望第二个参数是类型为`(String, String) -> Bool`的函数,因此实际上`String`,`String``Bool`类型并不需要作为闭包表达式定义中的一部分。 `sort`期望第二个参数是类型为`(String, String) -> Bool`的函数,因此实际上`String`,`String``Bool`类型并不需要作为闭包表达式定义中的一部分。
因为所有的类型都可以被正确推断,返回箭头 (`->`) 和围绕在参数周围的括号也可以被省略: 因为所有的类型都可以被正确推断,返回箭头 (`->`) 和围绕在参数周围的括号也可以被省略:
``` ```swift
reversed = sort(names, { s1, s2 in return s1 > s2 } ) reversed = sort(names, { s1, s2 in return s1 > s2 } )
``` ```
@ -139,7 +137,7 @@ reversed = sort(names, { s1, s2 in return s1 > s2 } )
单行表达式闭包可以通过隐藏`return`关键字来隐式返回单行表达式的结果,如上版本的例子可以改写为: 单行表达式闭包可以通过隐藏`return`关键字来隐式返回单行表达式的结果,如上版本的例子可以改写为:
``` ```swift
reversed = sort(names, { s1, s2 in s1 > s2 } ) reversed = sort(names, { s1, s2 in s1 > s2 } )
``` ```
@ -154,7 +152,7 @@ Swift 自动为内联函数提供了参数名称缩写功能,您可以直接
如果您在闭包表达式中使用参数名称缩写,您可以在闭包参数列表中省略对其的定义,并且对应参数名称缩写的类型会通过函数类型进行推断。 如果您在闭包表达式中使用参数名称缩写,您可以在闭包参数列表中省略对其的定义,并且对应参数名称缩写的类型会通过函数类型进行推断。
`in`关键字也同样可以被省略,因为此时闭包表达式完全由闭包函数体构成: `in`关键字也同样可以被省略,因为此时闭包表达式完全由闭包函数体构成:
``` ```swift
reversed = sort(names, { $0 > $1 } ) reversed = sort(names, { $0 > $1 } )
``` ```
@ -168,7 +166,7 @@ Swift 的`String`类型定义了关于大于号 (`>`) 的字符串实现,其
而这正好与`sort`函数的第二个参数需要的函数类型相符合。 而这正好与`sort`函数的第二个参数需要的函数类型相符合。
因此您可以简单地传递一个大于号Swift可以自动推断出您想使用大于号的字符串函数实现 因此您可以简单地传递一个大于号Swift可以自动推断出您想使用大于号的字符串函数实现
``` ```swift
reversed = sort(names, >) reversed = sort(names, >)
``` ```
@ -181,32 +179,28 @@ reversed = sort(names, >)
如果您需要将一个很长的闭包表达式作为最后一个参数传递给函数,可以使用尾随闭包来增强函数的可读性。 如果您需要将一个很长的闭包表达式作为最后一个参数传递给函数,可以使用尾随闭包来增强函数的可读性。
尾随闭包是一个书写在函数括号之后的闭包表达式,函数支持将其作为最后一个参数调用。 尾随闭包是一个书写在函数括号之后的闭包表达式,函数支持将其作为最后一个参数调用。
``` ```swift
func someFunctionThatTakesAClosure(closure: () -> ()) { func someFunctionThatTakesAClosure(closure: () -> ()) {
// 函数体部分 // 函数体部分
} }
// 以下是不使用尾随闭包进行函数调用 // 以下是不使用尾随闭包进行函数调用
someFunctionThatTakesAClosure({ someFunctionThatTakesAClosure({
// 闭包主体部分 // 闭包主体部分
}) })
// 以下是使用尾随闭包进行函数调用 // 以下是使用尾随闭包进行函数调用
someFunctionThatTakesAClosure() { someFunctionThatTakesAClosure() {
// 闭包主体部分 // 闭包主体部分
} }
``` ```
> 注意: > 注意:
>
> 如果函数只需要闭包表达式一个参数,当您使用尾随闭包时,您甚至可以把`()`省略掉。 > 如果函数只需要闭包表达式一个参数,当您使用尾随闭包时,您甚至可以把`()`省略掉。
在上例中作为`sort`函数参数的字符串排序闭包可以改写为: 在上例中作为`sort`函数参数的字符串排序闭包可以改写为:
``` ```swift
reversed = sort(names) { $0 > $1 } reversed = sort(names) { $0 > $1 }
``` ```
@ -219,7 +213,7 @@ reversed = sort(names) { $0 > $1 }
下例介绍了如何在`map`方法中使用尾随闭包将`Int`类型数组`[16,58,510]`转换为包含对应`String`类型的数组`["OneSix", "FiveEight", "FiveOneZero"]`: 下例介绍了如何在`map`方法中使用尾随闭包将`Int`类型数组`[16,58,510]`转换为包含对应`String`类型的数组`["OneSix", "FiveEight", "FiveOneZero"]`:
``` ```swift
let digitNames = [ let digitNames = [
0: "Zero", 1: "One", 2: "Two", 3: "Three", 4: "Four", 0: "Zero", 1: "One", 2: "Two", 3: "Three", 4: "Four",
5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine" 5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine"
@ -233,7 +227,7 @@ let numbers = [16, 58, 510]
您现在可以通过传递一个尾随闭包给`numbers``map`方法来创建对应的字符串版本数组。 您现在可以通过传递一个尾随闭包给`numbers``map`方法来创建对应的字符串版本数组。
需要注意的时调用`numbers.map`不需要在`map`后面包含任何括号,因为其只需要传递闭包表达式这一个参数,并且该闭包表达式参数通过尾随方式进行撰写: 需要注意的时调用`numbers.map`不需要在`map`后面包含任何括号,因为其只需要传递闭包表达式这一个参数,并且该闭包表达式参数通过尾随方式进行撰写:
``` ```swift
let strings = numbers.map { let strings = numbers.map {
(var number) -> String in (var number) -> String in
var output = "" var output = ""
@ -256,7 +250,6 @@ let strings = numbers.map {
其使用求余运算符 (number % 10) 计算最后一位数字并利用`digitNames`字典获取所映射的字符串。 其使用求余运算符 (number % 10) 计算最后一位数字并利用`digitNames`字典获取所映射的字符串。
> 注意: > 注意:
>
> 字典`digitNames`下标后跟着一个叹号 (!),因为字典下标返回一个可选值 (optional value),表明即使该 key 不存在也不会查找失败。 > 字典`digitNames`下标后跟着一个叹号 (!),因为字典下标返回一个可选值 (optional value),表明即使该 key 不存在也不会查找失败。
> 在上例中,它保证了`number % 10`可以总是作为一个`digitNames`字典的有效下标 key。 > 在上例中,它保证了`number % 10`可以总是作为一个`digitNames`字典的有效下标 key。
> 因此叹号可以用于强制解析 (force-unwrap) 存储在可选下标项中的`String`类型值。 > 因此叹号可以用于强制解析 (force-unwrap) 存储在可选下标项中的`String`类型值。
@ -287,7 +280,7 @@ Swift最简单的闭包形式是嵌套函数也就是定义在其他函数的
之后`makeIncrementor``incrementor`作为闭包返回。 之后`makeIncrementor``incrementor`作为闭包返回。
每次调用`incrementor`时,其会以`amount`作为增量增加`runningTotal`的值。 每次调用`incrementor`时,其会以`amount`作为增量增加`runningTotal`的值。
``` ```swift
func makeIncrementor(forIncrement amount: Int) -> () -> Int { func makeIncrementor(forIncrement amount: Int) -> () -> Int {
var runningTotal = 0 var runningTotal = 0
func incrementor() -> Int { func incrementor() -> Int {
@ -313,7 +306,7 @@ func makeIncrementor(forIncrement amount: Int) -> () -> Int {
如果我们单独看这个函数,会发现看上去不同寻常: 如果我们单独看这个函数,会发现看上去不同寻常:
``` ```swift
func incrementor() -> Int { func incrementor() -> Int {
runningTotal += amount runningTotal += amount
return runningTotal return runningTotal
@ -327,21 +320,20 @@ func incrementor() -> Int {
然而,因为每次调用该函数的时候都会修改`runningTotal`的值,`incrementor`捕获了当前`runningTotal`变量的引用,而不是仅仅复制该变量的初始值。捕获一个引用保证了当`makeIncrementor`结束时候并不会消失,也保证了当下一次执行`incrementor`函数时,`runningTotal`可以继续增加。 然而,因为每次调用该函数的时候都会修改`runningTotal`的值,`incrementor`捕获了当前`runningTotal`变量的引用,而不是仅仅复制该变量的初始值。捕获一个引用保证了当`makeIncrementor`结束时候并不会消失,也保证了当下一次执行`incrementor`函数时,`runningTotal`可以继续增加。
> 注意: > 注意:
>
> Swift 会决定捕获引用还是拷贝值。 > Swift 会决定捕获引用还是拷贝值。
> 您不需要标注`amount`或者`runningTotal`来声明在嵌入的`incrementor`函数中的使用方式。 > 您不需要标注`amount`或者`runningTotal`来声明在嵌入的`incrementor`函数中的使用方式。
> Swift 同时也处理`runingTotal`变量的内存管理操作,如果不再被`incrementor`函数使用,则会被清除。 > Swift 同时也处理`runingTotal`变量的内存管理操作,如果不再被`incrementor`函数使用,则会被清除。
下面代码为一个使用`makeIncrementor`的例子: 下面代码为一个使用`makeIncrementor`的例子:
``` ```swift
let incrementByTen = makeIncrementor(forIncrement: 10) let incrementByTen = makeIncrementor(forIncrement: 10)
``` ```
该例子定义了一个叫做`incrementByTen`的常量该常量指向一个每次调用会加10的`incrementor`函数。 该例子定义了一个叫做`incrementByTen`的常量该常量指向一个每次调用会加10的`incrementor`函数。
调用这个函数多次可以得到以下结果: 调用这个函数多次可以得到以下结果:
``` ```swift
incrementByTen() incrementByTen()
// 返回的值为10 // 返回的值为10
incrementByTen() incrementByTen()
@ -353,7 +345,7 @@ incrementByTen()
如果您创建了另一个`incrementor`,其会有一个属于自己的独立的`runningTotal`变量的引用。 如果您创建了另一个`incrementor`,其会有一个属于自己的独立的`runningTotal`变量的引用。
下面的例子中,`incrementBySevne`捕获了一个新的`runningTotal`变量,该变量和`incrementByTen`中捕获的变量没有任何联系: 下面的例子中,`incrementBySevne`捕获了一个新的`runningTotal`变量,该变量和`incrementByTen`中捕获的变量没有任何联系:
``` ```swift
let incrementBySeven = makeIncrementor(forIncrement: 7) let incrementBySeven = makeIncrementor(forIncrement: 7)
incrementBySeven() incrementBySeven()
// 返回的值为7 // 返回的值为7
@ -362,14 +354,12 @@ incrementByTen()
``` ```
> 注意: > 注意:
> > 如果您将闭包赋值给一个类实例的属性,并且该闭包通过指向该实例或其成员来捕获了该实例,您将创建一个在闭包和实例间的强引用环。
> 如果您闭包分配给一个类实例的属性,并且该闭包通过指向该实例或其成员来捕获了该实例,您将创建一个在闭包和实例间的强引用环。
> Swift 使用捕获列表来打破这种强引用环。更多信息,请参考 [闭包引起的循环强引用](../chapter2/16_Automatic_Reference_Counting.html#strong_reference_cycles_for_closures)。 > Swift 使用捕获列表来打破这种强引用环。更多信息,请参考 [闭包引起的循环强引用](../chapter2/16_Automatic_Reference_Counting.html#strong_reference_cycles_for_closures)。
<a name="closures_are_reference_types"></a> <a name="closures_are_reference_types"></a>
## 闭包是引用类型Closures Are Reference Types ## 闭包是引用类型Closures Are Reference Types
上面的例子中,`incrementBySeven``incrementByTen`是常量,但是这些常量指向的闭包仍然可以增加其捕获的变量值。 上面的例子中,`incrementBySeven``incrementByTen`是常量,但是这些常量指向的闭包仍然可以增加其捕获的变量值。
这是因为函数和闭包都是引用类型。 这是因为函数和闭包都是引用类型。
@ -378,12 +368,8 @@ incrementByTen()
这也意味着如果您将闭包赋值给了两个不同的常量/变量,两个值都会指向同一个闭包: 这也意味着如果您将闭包赋值给了两个不同的常量/变量,两个值都会指向同一个闭包:
``` ```swift
let alsoIncrementByTen = incrementByTen let alsoIncrementByTen = incrementByTen
alsoIncrementByTen() alsoIncrementByTen()
// 返回的值为50 // 返回的值为50
``` ```