diff --git a/source/chapter2/07_Closures.md b/source/chapter2/07_Closures.md index 7af7e57f..2f4b16ff 100644 --- a/source/chapter2/07_Closures.md +++ b/source/chapter2/07_Closures.md @@ -5,38 +5,38 @@ 本页内容包含: - 闭包表达式 -- Trailing闭包 +- 尾随闭包 - 值捕获 - 闭包是引用类型 -闭包是功能性自包含模块,可以在代码中被传递和使用。 -Swift 中的闭包与 C 和 Objective-C 中的`blocks`以及其他一些编程语言中的`lambdas`比较相似。 +闭包是自包含的函数代码块,可以在代码中被传递和使用。 +Swift 中的闭包与 C 和 Objective-C 中的`blocks` (代码块) 以及其他一些编程语言中的`lambdas` (匿名函数) 比较相似。 闭包可以**捕获**和存储其所在上下文中任意常量和变量的引用。 -这就是所谓的闭合并包裹着这些常量和变量,俗称闭包。Swift 会为您管理在**捕获**过程中涉及到的内存操作。 +这就是所谓的闭合并包裹着这些常量和变量,俗称闭包。Swift 会为您管理在**捕获**过程中涉及到的所有内存操作。 > 注意: -> 如果您不熟悉**捕获**(capturing) 这个概念也不用担心,后面会详细对其进行介绍。 +> 如果您不熟悉**捕获** (capturing) 这个概念也不用担心,您可以在 [捕获值](#capturing_values) 章节对其进行详细了解。 -在`函数`章节中介绍的全局和嵌套函数实际上也是特殊的闭包,闭包采取如下三种形式之一: +在`函数` (**这里需要函数章节提供相应链接进行配合**) 章节中介绍的全局和嵌套函数实际上也是特殊的闭包,闭包采取如下三种形式之一: * 全局函数是一个有名字但不会捕获任何值的闭包 * 嵌套函数是一个有名字并可以捕获其封闭函数域内值的闭包 -* 闭包表达式是一个利用轻量级语法所写的可以捕获其上下文中变量或常量值的没有名字的闭包 +* 闭包表达式是一个利用轻量级语法所写的可以捕获其上下文中变量或常量值的匿名闭包 Swift 的闭包表达式拥有简洁的风格,并鼓励在常见场景中进行语法优化,主要优化如下: * 利用上下文推断参数和返回值类型 -* 单表达式闭包可以省略`return`关键字 +* 隐式返回单表达式闭包,即单表达式闭包可以省略`return`关键字 * 参数名称缩写 -* Trailing 闭包语法 +* 尾随 (Trailing) 闭包语法 -### 闭包表达式 +### 闭包表达式 (Closure Expressions) --- -嵌套函数是一个在较复杂函数中方便进行命名和定义自包含代码模块的方式。 +嵌套函数 (**这里需要函数章节提供相应链接及锚点进行配合**) 是一个在较复杂函数中方便进行命名和定义自包含代码模块的方式。 当然,有时候撰写小巧的没有完整定义和命名的类函数结构也是很有用处的,尤其是在您处理一些函数并需要将另外一些函数作为该函数的参数时。 闭包表达式是一种利用简洁语法构建内联闭包的方式。 @@ -45,9 +45,9 @@ Swift 的闭包表达式拥有简洁的风格,并鼓励在常见场景中进 每一次迭代都用更简洁的方式描述了相同的功能。 -#####`sort`函数 +##### sort 函数 (The Sort Function) -Swift 标准库提供了`sort`函数,会根据您提供的排序闭包将已知类型数组中的值进行排序。 +Swift 标准库提供了`sort`函数,会根据您提供的基于输出类型排序的闭包函数将已知类型数组中的值进行排序。 一旦排序完成,函数会返回一个与原数组大小相同的新数组,该数组中包含已经正确排序的同类型元素。 下面的闭包表达式示例使用`sort`函数对一个 **String** 类型的数组进行字母逆序排序,以下是初始数组值: @@ -56,28 +56,33 @@ Swift 标准库提供了`sort`函数,会根据您提供的排序闭包将已 let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"] ``` -该例子对一个 **String** 类型的数组进行排序,因此排序闭包需为`(String, String) -> Bool`类型的函数。 +`sort`函数需要传入两个参数: -提供排序闭包的一种方式是撰写一个符合其类型要求的普通函数,并将其作为`sort`函数的第二个参数传入: +* 已知类型的数组 +* 闭包函数,该闭包函数需要传入与数组类型相同的两个值,并返回一个布尔类型值来告诉`sort`函数当排序结束后传入的第一个参数排在第二个参数前面还是后面。如果第一个参数值出现在第二个参数值前面,排序闭包函数需要返回`true`,反之返回`false`。 + +该例子对一个 **String** 类型的数组进行排序,因此排序闭包函数类型需为`(String, String) -> Bool`。 + +提供排序闭包函数的一种方式是撰写一个符合其类型要求的普通函数,并将其作为`sort`函数的第二个参数传入: ``` func backwards(s1: String, s2: String) -> Bool { return s1 > s2 } var reversed = sort(names, backwards) -// reversed is equal to ["Ewa", "Daniella", "Chris", "Barry", "Alex"] +// reversed 为 ["Ewa", "Daniella", "Chris", "Barry", "Alex"] ``` -如果第一个字符串 (s1) 大于第二个字符串 (s2),`backwards`函数则返回`true`,表示在新的数组中 s1 应该出现在 s2 前。 -字符中的 "大于" 表示 "按照字母顺序后出现"。 -这意味着字母 "B" 大于字母 "A", 字符串 "Tom" 大于字符串 "Tim"。 -其将进行字母逆序排序,"Barry" 将会排在 "Alex" 之后。 +如果第一个字符串 (`s1`) 大于第二个字符串 (`s2`),`backwards`函数返回`true`,表示在新的数组中`s1`应该出现在`s2`前。 +对于字符串中的字符来说,"大于" 表示 "按照字母顺序较晚出现"。 +这意味着字母`"B"`大于字母`"A"`,字符串`"Tom"`大于字符串`"Tim"`。 +其将进行字母逆序排序,`"Barry"`将会排在`"Alex"`之后。 然而,这是一个相当冗长的方式,本质上只是写了一个单表达式函数 (a > b)。 在下面的例子中,利用闭合表达式语法可以更好的构造一个内联排序闭包。 -##### 闭包表达式语法 +##### 闭包表达式语法 (Closure Expression Syntax) 闭包表达式语法有如下一般形式: @@ -87,8 +92,9 @@ var reversed = sort(names, backwards) } ``` -闭包表达式语法可以使用常量、变量和`inout`类型作为参数,不提供默认值。 -也可以在参数列表的最后使用可变参数。元组也可以作为参数和返回值。 +闭包表达式语法可以使用常量、变量和`inout` (**这里也需要函数章节提供相应链接和锚点进行配合**) 类型作为参数,不提供默认值。 +也可以在参数列表的最后使用可变参数。 +元组也可以作为参数和返回值。 下面的例子展示了之前`backwards`函数对应的闭包表达式版本的代码: @@ -99,7 +105,7 @@ reversed = sort(names, { (s1: String, s2: String) -> Bool in ``` 需要注意的是内联闭包参数和返回值类型声明与`backwards`函数类型声明相同。 -在这两种方式中,都写成了 (s1: String, s2: String) -> Bool。 +在这两种方式中,都写成了`(s1: String, s2: String) -> Bool`。 然而在内联闭包表达式中,函数和返回值类型都写在大括号内,而不是大括号外。 闭包的函数体部分由关键字`in`引入。 @@ -114,9 +120,9 @@ reversed = sort(names, { (s1: String, s2: String) -> Bool in return s1 > s2 } ) 这说明`sort`函数的整体调用保持不变,一对圆括号仍然包裹住了函数中整个参数集合。而其中一个参数现在变成了内联闭包 (相比于`backwards`版本的代码)。 -##### 根据上下文推断类型 +##### 根据上下文推断类型 (Inferring Type From Context) -因为排序闭包是作为函数的参数进行传入的,Swift可以推断其参数和返回值的类型。 +因为排序闭包函数是作为`sort`函数的参数进行传入的,Swift可以推断其参数和返回值的类型。 `sort`期望第二个参数是类型为`(String, String) -> Bool`的函数,因此实际上`String`,`String`和`Bool`类型并不需要作为闭包表达式定义中的一部分。 因为所有的类型都可以被正确推断,返回箭头 (`->`) 和围绕在参数周围的括号也可以被省略: @@ -127,7 +133,7 @@ reversed = sort(names, { s1, s2 in return s1 > s2 } ) 实际上任何情况下,通过内联闭包表达式构造的闭包作为参数传递给函数时,都可以推断出闭包的参数和返回值类型,这意味着您几乎不需要利用完整格式构造任何内联闭包。 -##### 单行表达式闭包可以省略`return` +##### 单表达式闭包隐式返回 (Implicit Return From Single-Expression Clossures) 单行表达式闭包可以通过隐藏`return`关键字来隐式返回单行表达式的结果,如上版本的例子可以改写为: @@ -136,10 +142,10 @@ reversed = sort(names, { s1, s2 in s1 > s2 } ) ``` 在这个例子中,`sort`函数的第二个参数函数类型明确了闭包必须返回一个 **Bool** 类型值。 -因为闭包函数体只包含了一个单一表达式 (s1 > s2),该表达式返回 **Bool** 类型值,因此这里没有歧义,`return`关键字可以省略。 +因为闭包函数体只包含了一个单一表达式 (`s1 > s2`),该表达式返回 **Bool** 类型值,因此这里没有歧义,`return`关键字可以省略。 -##### 参数名称缩写 +##### 参数名称缩写 (Shorthand Argument Names) Swift 自动为内联函数提供了参数名称缩写功能,您可以直接通过`$0`,`$1`,`$2`来顺序调用闭包的参数。 @@ -153,10 +159,10 @@ reversed = sort(names, { $0 > $1 } ) 在这个例子中,`$0`和`$1`表示闭包中第一个和第二个 **String** 类型的参数。 -##### 运算符函数 +##### 运算符函数 (Operator Functions) 实际上还有一种更简短的方式来撰写上面例子中的闭包表达式。 -Swift 的 **String** 类型定义了关于大于号 (>) 的字符串实现,其作为一个函数接受两个 **String** 类型的参数并返回 **Bool** 类型的值。 +Swift 的 **String** 类型定义了关于大于号 (`>`) 的字符串实现,其作为一个函数接受两个 **String** 类型的参数并返回 **Bool** 类型的值。 而这正好与`sort`函数的第二个参数需要的函数类型相符合。 因此,您可以简单地传递一个大于号,Swift可以自动推断出您想使用大于号的字符串函数实现: @@ -164,35 +170,35 @@ Swift 的 **String** 类型定义了关于大于号 (>) 的字符串实现,其 reversed = sort(names, >) ``` -更多关于运算符表达式的内容请查看 [Operator Functions](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/AdvancedOperators.html#//apple_ref/doc/uid/TP40014097-CH27-XID_43) 。 +更多关于运算符表达式的内容请查看 [Operator Functions]() (**这里需要 Operator Functions 进行配合**)。 -### Trailing 闭包 +### 尾随闭包 (Trailing Closures) --- -如果您需要将一个很长的闭包表达式作为最后一个参数传递给函数,可以使用 trailing 闭包来增强函数的可读性。 -Trailing 闭包是一个书写在函数括号之外(之后)的闭包表达式,函数支持将其作为最后一个参数调用。 +如果您需要将一个很长的闭包表达式作为最后一个参数传递给函数,可以使用尾随闭包来增强函数的可读性。 +尾随闭包是一个书写在函数括号之外(之后)的闭包表达式,函数支持将其作为最后一个参数调用。 ``` func someFunctionThatTakesAClosure(closure: () -> ()) { // 函数体部分 } -// 以下是不使用 trailing 闭包进行函数调用 +// 以下是不使用尾随闭包进行函数调用 someFunctionThatTakesAClosure({ // 闭包主体部分 }) -// 以下是使用 trailing 闭包进行函数调用 +// 以下是使用尾随闭包进行函数调用 someFunctionThatTakesAClosure() { // 闭包主体部分 } ``` > 注意: -> 如果函数只需要闭包表达式一个参数,当您使用 trailing 闭包时,您甚至可以把 () 省略掉。 +> 如果函数只需要闭包表达式一个参数,当您使用尾随闭包时,您甚至可以把 () 省略掉。 NOTE 在上例中作为`sort`函数参数的字符串排序闭包可以改写为: @@ -201,14 +207,14 @@ NOTE reversed = sort(names) { $0 > $1 } ``` -当闭包非常长以至于不能在一行中进行书写时,Trailing 闭包变得非常有用。 +当闭包非常长以至于不能在一行中进行书写时,尾随闭包变得非常有用。 举例来说,Swift 的 **Array** 类型有一个`map`方法,其获取一个闭包表达式作为其唯一参数。 数组中的每一个元素调用一次该闭包函数,并返回该元素所映射的值(也可以是不同类型的值)。 具体的映射方式和返回值类型由闭包来指定。 当提供给数组闭包函数后,`map`方法将返回一个新的数组,数组中包含了与原数组一一对应的映射后的值。 -下例介绍了如何在`map`方法中使用 trailing 闭包将 **Int** 类型数组`[16,58,510]`转换为包含对应 **String** 类型的数组`["OneSix", "FiveEight", "FiveOneZero"]`: +下例介绍了如何在`map`方法中使用尾随闭包将 **Int** 类型数组`[16,58,510]`转换为包含对应 **String** 类型的数组`["OneSix", "FiveEight", "FiveOneZero"]`: ``` let digitNames = [ @@ -221,8 +227,8 @@ let numbers = [16, 58, 510] 如上代码创建了一个数字位和他们名字映射的英文版本字典。 同时定义了一个准备转换为字符串的整型数组。 -您现在可以通过传递一个 trailing 闭包给`numbers`的`map`方法来创建对应的字符串版本数组。 -需要注意的时调用`numbers.map`不需要在`map`后面包含任何括号,因为其只需要传递闭包表达式这一个参数,并且该闭包表达式参数通过 trailing 方式进行撰写: +您现在可以通过传递一个尾随闭包给`numbers`的`map`方法来创建对应的字符串版本数组。 +需要注意的时调用`numbers.map`不需要在`map`后面包含任何括号,因为其只需要传递闭包表达式这一个参数,并且该闭包表达式参数通过尾随方式进行撰写: ``` let strings = numbers.map { @@ -241,7 +247,7 @@ let strings = numbers.map { `map`在数组中为每一个元素调用了闭包表达式。 您不需要指定闭包的输入参数`number`的类型,因为可以通过要映射的数组类型进行推断。 -闭包`number`参数被声明为一个变量参数 (变量的具体描述请参看[Constant and Variable Parameters](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Functions.html#//apple_ref/doc/uid/TP40014097-CH10-XID_224)),因此可以在闭包函数体内对其进行修改。 +闭包`number`参数被声明为一个变量参数 (变量的具体描述请参看[Constant and Variable Parameters]()(**这里需要Closure Expression Syntax进行配合**)),因此可以在闭包函数体内对其进行修改。 闭包表达式制定了返回类型为 **String**,以表明存储映射值的新数组类型为 **String**。 闭包表达式在每次被调用的时候创建了一个字符串并返回。 @@ -250,7 +256,7 @@ let strings = numbers.map { > 注意: > 字典`digitNames`下标后跟着一个叹号 (!),因为字典下标返回一个可选值 (optional value),表明即使该 key 不存在也不会查找失败。 > 在上例中,它保证了`number % 10`可以总是作为一个`digitNames`字典的有效下标 key。 -> 因此叹号可以用于强制展开 (force-unwrap) 存储在可选下标项中的 **String** 类型值。 +> 因此叹号可以用于强制解析 (force-unwrap) 存储在可选下标项中的 **String** 类型值。 从`digitNames`字典中获取的字符串被添加到输出的前部,逆序建立了一个字符串版本的数字。 (在表达式`number % 10`中,如果number为16,则返回6,58返回8,510返回0)。 @@ -259,12 +265,12 @@ let strings = numbers.map { 因为其是整数,在计算过程中未除尽部分被忽略。 因此 16变成了1,58变成了5,510变成了51。 -整个过程重复进行,直到`number /= 10`为0,这时闭包会将字符串输出,而map函数则会将字符串添加到所映射的数组中。 +整个过程重复进行,直到`number /= 10`为0,这时闭包会将字符串输出,而`map`函数则会将字符串添加到所映射的数组中。 -上例中 trailing 闭包语法在函数后整洁封装了具体的闭包功能,而不再需要将整个闭包包裹在`map`函数的括号内。 +上例中尾随闭包语法在函数后整洁封装了具体的闭包功能,而不再需要将整个闭包包裹在`map`函数的括号内。 -### 捕获 (Caputure) 值 +### 捕获值 (Capturing Values) --- @@ -293,7 +299,7 @@ func makeIncrementor(forIncrement amount: Int) -> () -> Int { `makeIncrementor`返回类型为`() -> Int`。 这意味着其返回的是一个函数,而不是一个简单类型值。 该函数在每次调用时不接受参数只返回一个 **Int** 类型的值。 -关于函数返回其他函数的内容,请查看[Function Types as Return Types](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Functions.html#//apple_ref/doc/uid/TP40014097-CH10-XID_232)。 +关于函数返回其他函数的内容,请查看[Function Types as Return Types]()(**需要函数章节进行配合**)。 `makeIncrementor`函数定义了一个整型变量`runningTotal`(初始为0) 用来存储当前跑步总数。 该值通过`incrementor`返回。 @@ -323,7 +329,7 @@ func incrementor() -> Int { > 您不需要标注`amount`或者`runningTotal`来声明在嵌入的`incrementor`函数中的使用方式。 > Swift 同时也处理`runingTotal`变量的内存管理操作,如果不再被`incrementor`函数使用,则会被清除。 -下面为一个使用`makeIncrementor`的例子: +下面代码为一个使用`makeIncrementor`的例子: ``` let incrementByTen = makeIncrementor(forIncrement: 10) @@ -354,7 +360,7 @@ incrementByTen() > 注意: > 如果您闭包分配给一个类实例的属性,并且该闭包通过指向该实例或其成员来捕获了该实例,您将创建一个在闭包和实例间的强引用环。 -> Swift 使用捕获列表来打破这种强引用环。更多信息,请参考 [Strong Reference Cycles for Closures](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/AutomaticReferenceCounting.html#//apple_ref/doc/uid/TP40014097-CH20-XID_61)。 +> Swift 使用捕获列表来打破这种强引用环。更多信息,请参考 [Strong Reference Cycles for Closures]()(**需要ARC章节进行配合**)。 ### 闭包是引用类型