Closures (闭包)

Closuresare self-contained blocks of functionality that can be passed around and used in your code. Closures in Swift are similar to blocks in C and Objective-C and to lambdas in other programming languages.

闭包是自包含的函数代码块,可以在代码中被传递和使用。Swift 中的闭包与 C 和 Objective-C 中的代码块(blocks)以及其他一些编程语言中的匿名函数比较相似。

Closures can capture and store references to any constants and variables from the context in which they are defined. This is known asclosing overthose constants and variables. Swift handles all of the memory management of capturing for you.

闭包可以捕获和存储其所在上下文中任意常量和变量的引用。被称为包裹常量和变量。 Swift 会为你管理在捕获过程中涉及到的所有内存操作。

Note

Don’t worry if you are not familiar with the concept of capturing. It is explained in detail below inCapturing Values.

如果你不熟悉捕获(capturing)这个概念也不用担心,你可以在值捕获章节对其进行详细了解。

Global and nested functions, as introduced inFunctions, are actually special cases of closures. Closures take one of three forms:

函数章节中介绍的全局和嵌套函数实际上也是特殊的闭包,闭包采取如下三种形式之一:

1. Global functions are closures that have a name and do not capture any values.

全局函数是一个有名字但不会捕获任何值的闭包

2. Nested functions are closures that have a name and can capture values from their enclosing function.

嵌套函数是一个有名字并可以捕获其封闭函数域内值的闭包

3. Closure expressions are unnamed closures written in a lightweight syntax that can capture values from their surrounding context.

闭包表达式是一个利用轻量级语法所写的可以捕获其上下文中变量或常量值的匿名闭包

Swift’s closure expressions have a clean, clear style, with optimizations that encourage brief, clutter-free syntax in common scenarios. These optimizations include:

Swift 的闭包表达式拥有简洁的风格,并鼓励在常见场景中进行语法优化,主要优化如下:

1. Inferring parameter and return value types from context

利用上下文推断参数和返回值类型

2. Implicit returns from single-expression closures

隐式返回单表达式闭包,即单表达式闭包可以省略return关键字

3. Shorthand argument names

参数名称缩写

4. Trailing closure syntax

尾随闭包语法

Closure Expressions (闭包表达式)

Nested functions, as introduced inNested Functions, are a convenient means of naming and defining self-contained blocks of code as part of a larger function. However, it is sometimes useful to write shorter versions of function-like constructs without a full declaration and name. This is particularly true when you work with functions or methods that take functions as one or more of their arguments.

嵌套函数是一个在较复杂函数中方便进行命名和定义自包含代码模块的方式。当然,有时候编写小巧的没有完整定义和命名的类函数结构也是很有用处的,尤其是在你处理一些函数并需要将另外一些函数作为该函数的参数时。

Closure expressionsare a way to write inline closures in a brief, focused syntax. Closure expressions provide several syntax optimizations for writing closures in a shortened form without loss of clarity or intent. The closure expression examples below illustrate these optimizations by refining a single example of thesorted(by:)method over several iterations, each of which expresses the same functionality in a more succinct way.

闭包表达式是一种利用简洁语法构建内联闭包的方式。闭包表达式提供了一些语法优化,使得撰写闭包变得简单明了。下面闭包表达式的例子通过使用几次迭代展示了sorted(by:)方法定义和语法优化的方式。每一次迭代都用更简洁的方式描述了相同的功能。

The Sorted Method (sorted 方法)

Swift’s standard library provides a method calledsorted(by:), which sorts an array of values of a known type, based on the output of a sorting closure that you provide. Once it completes the sorting process, thesorted(by:)method returns a new array of the same type and size as the old one, with its elements in the correct sorted order. The original array is not modified by thesorted(by:)method.

Swift 标准库提供了名为sorted(by:)的方法,它会根据你所提供的用于排序的闭包函数将已知类型数组中的值进行排序。一旦排序完成,sorted(by:)方法会返回一个与原数组大小相同,包含同类型元素且元素已正确排序的新数组。原数组不会被sorted(by:)方法修改。

The closure expression examples below use thesorted(by:)method to sort an array ofStringvalues in reverse alphabetical order. Here’s the initial array to be sorted:

下面的闭包表达式示例使用sorted(by:)方法对一个String类型的数组进行字母逆序排序。以下是初始数组:

let names= ["Chris", "Alex", "Ewa", "Barry", "Daniella"]

Thesorted(by:)method accepts a closure that takes two arguments of the same type as the array’s contents, and returns aBoolvalue to say whether the first value should appear before or after the second value once the values are sorted. The sorting closure needs to returntrueif the first value should appearbeforethe second value, andfalseotherwise.

sorted(by:)方法接受一个闭包,该闭包函数需要传入与数组元素类型相同的两个值,并返回一个布尔类型值来表明当排序结束后传入的第一个参数排在第二个参数前面还是后面。如果第一个参数值出现在第二个参数值前面,排序闭包函数需要返回true,反之返回false。

This example is sorting an array ofStringvalues, and so the sorting closure needs to be a function of type(String, String) -> Bool.

该例子对一个String类型的数组进行排序,因此排序闭包函数类型需为(String, String) -> Bool。

One way to provide the sorting closure is to write a normal function of the correct type, and to pass it in as an argument to thesorted(by:)method:

提供排序闭包函数的一种方式是撰写一个符合其类型要求的普通函数,并将其作为sorted(by:)方法的参数传入:

func backward (_s1:String,_s2:String) ->Bool{

    returns1>s2

}

var reversedNames = names.sorted(by:backward)

// reversedNames is equal to ["Ewa", "Daniella", "Chris", "Barry", "Alex"]

If the first string (s1) is greater than the second string (s2), thebackward(_:_:)function will returntrue, indicating thats1should appear befores2in the sorted array. For characters in strings, “greater than” means “appears later in the alphabet than”. This means that the letter"B"is “greater than” the letter"A", and the string"Tom"is greater than the string"Tim". This gives a reverse alphabetical sort, with"Barry"being placed before"Alex", and so on.

如果第一个字符串(s1)大于第二个字符串(s2),backward(_:_:)函数会返回true,表示在新的数组中s1应该出现在s2前。对于字符串中的字符来说,“大于”表示“按照字母顺序较晚出现”。这意味着字母"B"大于字母"A",字符串"Tom"大于字符串"Tim"。该闭包将进行字母逆序排序,"Barry"将会排在"Alex"之前。

However, this is a rather long-winded way to write what is essentially a single-expression function (a > b). In this example, it would be preferable to write the sorting closure inline, using closure expression syntax.

然而,以这种方式来编写一个实际上很简单的表达式(a > b),确实太过繁琐了。对于这个例子来说,利用闭包表达式语法可以更好地构造一个内联排序闭包。

Closure Expression Syntax (闭包表达式语法)

Closure expression syntax has the following general form:

闭包表达式语法有如下的一般形式:

{ (parameters) ->return typein

    statements

}

Theparametersin closure expression syntax can be in-out parameters, but they can’t have a default value. Variadic parameters can be used if you name the variadic parameter. Tuples can also be used as parameter types and return types.

闭包表达式参数可以是 in-out 参数,但不能设定默认值。也可以使用具名的可变参数(译者注:但是如果可变参数不放在参数列表的最后一位的话,调用闭包的时时编译器将报错。可参考这里)。元组也可以作为参数和返回值。

The example below shows a closure expression version of thebackward(_:_:)function from earlier:

下面的例子展示了之前backward(_:_:)函数对应的闭包表达式版本的代码:

reversedNames = names.sorted(by: { (s1:String,s2:String) ->Boolin

    returns1>s2

})

Note that the declaration of parameters and return type for this inline closure is identical to the declaration from thebackward(_:_:)function. In both cases, it is written as(s1: String, s2: String) -> Bool. However, for the inline closure expression, the parameters and return type are writteninsidethe curly braces, not outside of them.

需要注意的是内联闭包参数和返回值类型声明与backward(_:_:)函数类型声明相同。在这两种方式中,都写成了(s1: String, s2: String) -> Bool。然而在内联闭包表达式中,函数和返回值类型都写在大括号内,而不是大括号外。

The start of the closure’s body is introduced by theinkeyword. This keyword indicates that the definition of the closure’s parameters and return type has finished, and the body of the closure is about to begin.

闭包的函数体部分由关键字in引入。该关键字表示闭包的参数和返回值类型定义已经完成,闭包函数体即将开始。

Because the body of the closure is so short, it can even be written on a single line:

由于这个闭包的函数体部分如此短,以至于可以将其改写成一行代码:

reversedNames = names.sorted(by: { (s1:String,s2:String) ->Boolinreturns1>s2} )

This illustrates that the overall call to thesorted(by:)method has remained the same. A pair of parentheses still wrap the entire argument for the method. However, that argument is now an inline closure.

该例中sorted(by:)方法的整体调用保持不变,一对圆括号仍然包裹住了方法的整个参数。然而,参数现在变成了内联闭包。

Inferring Type From Context (根据上下文推断类型)

Because the sorting closure is passed as an argument to a method, Swift can infer the types of its parameters and the type of the value it returns. Thesorted(by:)method is being called on an array of strings, so its argument must be a function of type(String, String) -> Bool. This means that the(String, String)andBooltypes do not need to be written as part of the closure expression’s definition. Because all of the types can be inferred, the return arrow (->) and the parentheses around the names of the parameters can also be omitted:

因为排序闭包函数是作为sorted(by:)方法的参数传入的,Swift 可以推断其参数和返回值的类型。sorted(by:)方法被一个字符串数组调用,因此其参数必须是(String, String) -> Bool类型的函数。这意味着(String, String)和Bool类型并不需要作为闭包表达式定义的一部分。因为所有的类型都可以被正确推断,返回箭头(->)和围绕在参数周围的括号也可以被省略:

reversedNames = names.sorted(by: {s1,s2inreturns1>s2} )

It is always possible to infer the parameter types and return type when passing a closure to a function or method as an inline closure expression. As a result, you never need to write an inline closure in its fullest form when the closure is used as a function or method argument.

实际上,通过内联闭包表达式构造的闭包作为参数传递给函数或方法时,总是能够推断出闭包的参数和返回值类型。这意味着闭包作为函数或者方法的参数时,你几乎不需要利用完整格式构造内联闭包。

Nonetheless, you can still make the types explicit if you wish, and doing so is encouraged if it avoids ambiguity for readers of your code. In the case of thesorted(by:)method, the purpose of the closure is clear from the fact that sorting is taking place, and it is safe for a reader to assume that the closure is likely to be working withStringvalues, because it is assisting with the sorting of an array of strings.

尽管如此,你仍然可以明确写出有着完整格式的闭包。如果完整格式的闭包能够提高代码的可读性,则我们更鼓励采用完整格式的闭包。而在sorted(by:)方法这个例子里,显然闭包的目的就是排序。由于这个闭包是为了处理字符串数组的排序,因此读者能够推测出这个闭包是用于字符串处理的。

Implicit Returns from Single-Expression Closures (单表达式闭包隐式返回)

Single-expression closures can implicitly return the result of their single expression by omitting thereturnkeyword from their declaration, as in this version of the previous example:

单行表达式闭包可以通过省略return关键字来隐式返回单行表达式的结果,如上版本的例子可以改写为:

reversedNames = names.sorted(by: {s1,s2ins1>s2} )

Here, the function type of thesorted(by:)method’s argument makes it clear that aBoolvalue must be returned by the closure. Because the closure’s body contains a single expression (s1 > s2) that returns aBoolvalue, there is no ambiguity, and thereturnkeyword can be omitted.

在这个例子中,sorted(by:)方法的参数类型明确了闭包必须返回一个Bool类型值。因为闭包函数体只包含了一个单一表达式(s1 > s2),该表达式返回Bool类型值,因此这里没有歧义,return关键字可以省略。

Shorthand Argument Names (参数名称缩写)

Swift automatically provides shorthand argument names to inline closures, which can be used to refer to the values of the closure’s arguments by the names$0,$1,$2, and so on.

Swift 自动为内联闭包提供了参数名称缩写功能,你可以直接通过$0,$1,$2来顺序调用闭包的参数,以此类推。

If you use these shorthand argument names within your closure expression, you can omit the closure’s argument list from its definition, and the number and type of the shorthand argument names will be inferred from the expected function type. Theinkeyword can also be omitted, because the closure expression is made up entirely of its body:

如果你在闭包表达式中使用参数名称缩写,你可以在闭包定义中省略参数列表,并且对应参数名称缩写的类型会通过函数类型进行推断。in关键字也同样可以被省略,因为此时闭包表达式完全由闭包函数体构成:

reversedNames = names.sorted(by: {$0>$1} )

Here,$0 and $1 refer to the closure’s first and secondStringarguments.

在这个例子中,$0和$1表示闭包中第一个和第二个String类型的参数。

Operator Methods (运算符方法)

There’s actually an evenshorterway to write the closure expression above. Swift’sStringtype defines its string-specific implementation of the greater-than operator (>) as a method that has two parameters of typeString, and returns a value of typeBool. This exactly matches the method type needed by thesorted(by:)method. Therefore, you can simply pass in the greater-than operator, and Swift will infer that you want to use its string-specific implementation:

实际上还有一种更简短的方式来编写上面例子中的闭包表达式。Swift 的String类型定义了关于大于号(>)的字符串实现,其作为一个函数接受两个String类型的参数并返回Bool类型的值。而这正好与sorted(by:)方法的参数需要的函数类型相符合。因此,你可以简单地传递一个大于号,Swift 可以自动推断出你想使用大于号的字符串函数实现:

reversedNames = names.sorted(by: >)

For more about operator method, seeOperator Methods.

更多关于运算符方法的内容请查看运算符方法

Trailing Closures (尾随闭包)

If you need to pass a closure expression to a function as the function’s final argument and the closure expression is long, it can be useful to write it as atrailing closureinstead. A trailing closure is written after the function call’s parentheses, even though it is still an argument to the function. When you use the trailing closure syntax, you don’t write the argument label for the closure as part of the function call.

如果你需要将一个很长的闭包表达式作为最后一个参数传递给函数,可以使用尾随闭包来增强函数的可读性。尾随闭包是一个书写在函数括号之后的闭包表达式,函数支持将其作为最后一个参数调用。在使用尾随闭包时,你不用写出它的参数标签:

func someFunctionThatTakesAClosure(closure: () ->Void) {

    // function body goes here

}

// Here's how you call this function without using a trailing closure:

// 以下是不使用尾随闭包进行函数调用

someFunctionThatTakesAClosure(closure: {

// closure's body goes here

})

// Here's how you call this function with a trailing closure instead:

// 以下是使用尾随闭包进行函数调用

someFunctionThatTakesAClosure() {

// trailing closure's body goes here

// 闭包主体部分

}

The string-sorting closure from theClosure Expression Syntaxsection above can be written outside of thesorted(by:)method’s parentheses as a trailing closure:

闭包表达式语法一节中作为sorted(by:)方法参数的字符串排序闭包可以改写为:

reversedNames = names.sorted() {$0>$1}

If a closure expression is provided as the function or method’s only argument and you provide that expression as a trailing closure, you do not need to write a pair of parentheses()after the function or method’s name when you call the function:

如果闭包表达式是函数或方法的唯一参数,则当你使用尾随闭包时,你甚至可以把()省略掉:

reversedNames = names.sorted{$0>$1}

Trailing closures are most useful when the closure is sufficiently long that it is not possible to write it inline on a single line. As an example, Swift’sArraytype has amap(_:)method which takes a closure expression as its single argument. The closure is called once for each item in the array, and returns an alternative mapped value (possibly of some other type) for that item. The nature of the mapping and the type of the returned value is left up to the closure to specify.

当闭包非常长以至于不能在一行中进行书写时,尾随闭包变得非常有用。举例来说,Swift 的Array类型有一个map(_:)方法,这个方法获取一个闭包表达式作为其唯一参数。该闭包函数会为数组中的每一个元素调用一次,并返回该元素所映射的值。具体的映射方式和返回值类型由闭包来指定。

After applying the provided closure to each array element, themap(_:)method returns a new array containing all of the new mapped values, in the same order as their corresponding values in the original array.

当提供给数组的闭包应用于每个数组元素后,map(_:)方法将返回一个新的数组,数组中包含了与原数组中的元素一一对应的映射后的值。

Here’s how you can use themap(_:)method with a trailing closure to convert an array ofIntvalues into an array ofStringvalues. The array[16, 58, 510]is used to create the new array["OneSix", "FiveEight", "FiveOneZero"]:

下例介绍了如何在map(_:)方法中使用尾随闭包将Int类型数组[16, 58, 510]转换为包含对应String类型的值的数组["OneSix", "FiveEight", "FiveOneZero"]:

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]

The code above creates a dictionary of mappings between the integer digits and English-language versions of their names. It also defines an array of integers, ready to be converted into strings.

如上代码创建了一个整型数位和它们英文版本名字相映射的字典。同时还定义了一个准备转换为字符串数组的整型数组。

You can now use thenumbersarray to create an array ofStringvalues, by passing a closure expression to the array’smap(_:)method as a trailing closure:

你现在可以通过传递一个尾随闭包给numbers数组的map(_:)方法来创建对应的字符串版本数组:

let strings=numbers.map{

    (number) ->Stringin

    varnumber=number

    var output=""

  repeat{

  output=digitNames[number%10]! +output

    number/=10

} while number>0

   return output

}

// strings is inferred to be of type [String]

// strings 常量被推断为字符串类型数组,即 [String]

// its value is ["OneSix", "FiveEight", "FiveOneZero"]

Themap(_:)method calls the closure expression once for each item in the array. You do not need to specify the type of the closure’s input parameter,number, because the type can be inferred from the values in the array to be mapped.

map(_:)为数组中每一个元素调用了一次闭包表达式。你不需要指定闭包的输入参数number的类型,因为可以通过要映射的数组类型进行推断。

In this example, the variablenumberis initialized with the value of the closure’snumberparameter, so that the value can be modified within the closure body. (The parameters to functions and closures are always constants.) The closure expression also specifies a return type ofString, to indicate the type that will be stored in the mapped output array.

在该例中,局部变量number的值由闭包中的number参数获得,因此可以在闭包函数体内对其进行修改,(闭包或者函数的参数总是常量),闭包表达式指定了返回类型为String,以表明存储映射值的新数组类型为String。

The closure expression builds a string calledoutputeach time it is called. It calculates the last digit ofnumberby using the remainder operator (number % 10), and uses this digit to look up an appropriate string in thedigitNamesdictionary. The closure can be used to create a string representation of any integer greater than zero.

闭包表达式在每次被调用的时候创建了一个叫做output的字符串并返回。其使用求余运算符(number % 10)计算最后一位数字并利用digitNames字典获取所映射的字符串。这个闭包能够用于创建任意正整数的字符串表示。

Note

The call to thedigitNamesdictionary’s subscript is followed by an exclamation mark (!), because dictionary subscripts return an optional value to indicate that the dictionary lookup can fail if the key does not exist. In the example above, it is guaranteed thatnumber % 10will always be a valid subscript key for thedigitNamesdictionary, and so an exclamation mark is used to force-unwrap theStringvalue stored in the subscript’s optional return value.

字典digitNames下标后跟着一个叹号(!),因为字典下标返回一个可选值(optional value),表明该键不存在时会查找失败。在上例中,由于可以确定number % 10总是digitNames字典的有效下标,因此叹号可以用于强制解包 (force-unwrap) 存储在下标的可选类型的返回值中的String类型的值。

The string retrieved from thedigitNamesdictionary is added to thefrontofoutput, effectively building a string version of the number in reverse. (The expressionnumber % 10gives a value of6for16,8for58, and0for510.)

从digitNames字典中获取的字符串被添加到output的前部,逆序建立了一个字符串版本的数字。(在表达式number % 10中,如果number为16,则返回6,58返回8,510返回0。)

Thenumbervariable is then divided by10. Because it is an integer, it is rounded down during the division, so16becomes1,58becomes5, and510becomes51.

number变量之后除以10。因为其是整数,在计算过程中未除尽部分被忽略。因此16变成了1,58变成了5,510变成了51。

The process is repeated untilnumberis equal to0, at which point theoutputstring is returned by the closure, and is added to the output array by themap(_:)method.

整个过程重复进行,直到number /= 10为0,这时闭包会将字符串output返回,而map(_:)方法则会将字符串添加到映射数组中。

The use of trailing closure syntax in the example above neatly encapsulates the closure’s functionality immediately after the function that closure supports, without needing to wrap the entire closure within themap(_:)method’s outer parentheses.

在上面的例子中,通过尾随闭包语法,优雅地在函数后封装了闭包的具体功能,而不再需要将整个闭包包裹在map(_:)方法的括号内。

Capturing Values (值捕获)

A closure cancaptureconstants and variables from the surrounding context in which it is defined. The closure can then refer to and modify the values of those constants and variables from within its body, even if the original scope that defined the constants and variables no longer exists.

闭包可以在其被定义的上下文中捕获常量或变量。即使定义这些常量和变量的原作用域已经不存在,闭包仍然可以在闭包函数体内引用和修改这些值。

In Swift, the simplest form of a closure that can capture values is a nested function, written within the body of another function. A nested function can capture any of its outer function’s arguments and can also capture any constants and variables defined within the outer function.

Swift 中,可以捕获值的闭包的最简单形式是嵌套函数,也就是定义在其他函数的函数体内的函数。嵌套函数可以捕获其外部函数所有的参数以及定义的常量和变量。

Here’s an example of a function calledmakeIncrementer, which contains a nested function calledincrementer. The nestedincrementer()function captures two values,runningTotalandamount, from its surrounding context. After capturing these values,incrementeris returned bymakeIncrementeras a closure that incrementsrunningTotalbyamounteach time it is called.

举个例子,这有一个叫做makeIncrementer的函数,其包含了一个叫做incrementer的嵌套函数。嵌套函数incrementer()从上下文中捕获了两个值,runningTotal和amount。捕获这些值之后,makeIncrementer将incrementer作为闭包返回。每次调用incrementer时,其会以amount作为增量增加runningTotal的值。

func makeIncrementer(forIncrementamount:Int) -> () ->Int{

    var runningTotal=0

    func incrementer() ->Int{

        runningTotal+=amount

        return runningTotal

}

return incrementer

 }

The return type ofmakeIncrementeris() -> Int. This means that it returns afunction, rather than a simple value. The function it returns has no parameters, and returns anIntvalue each time it is called. To learn how functions can return other functions, seeFunction Types as Return Types.

makeIncrementer返回类型为() -> Int。这意味着其返回的是一个函数,而非一个简单类型的值。该函数在每次调用时不接受参数,只返回一个Int类型的值。关于函数返回其他函数的内容,请查看函数类型作为返回类型

ThemakeIncrementer(forIncrement:)function defines an integer variable calledrunningTotal, to store the current running total of the incrementer that will be returned. This variable is initialized with a value of0.

makeIncrementer(forIncrement:)函数定义了一个初始值为0的整型变量runningTotal,用来存储当前总计数值。该值为incrementer的返回值。

ThemakeIncrementer(forIncrement:)function has a singleIntparameter with an argument label offorIncrement, and a parameter name ofamount. The argument value passed to this parameter specifies how muchrunningTotalshould be incremented by each time the returned incrementer function is called. ThemakeIncrementerfunction defines a nested function calledincrementer, which performs the actual incrementing. This function simply addsamounttorunningTotal, and returns the result.

makeIncrementer(forIncrement:)有一个Int类型的参数,其外部参数名为forIncrement,内部参数名为amount,该参数表示每次incrementer被调用时runningTotal将要增加的量。makeIncrementer函数还定义了一个嵌套函数incrementer,用来执行实际的增加操作。该函数简单地使runningTotal增加amount,并将其返回。

When considered in isolation, the nestedincrementer()function might seem unusual:

如果我们单独考虑嵌套函数incrementer(),会发现它有些不同寻常:

func incrementer() ->Int{

    runningTotal+=amount

    return runningTotal

}

Theincrementer()function doesn’t have any parameters, and yet it refers torunningTotalandamountfrom within its function body. It does this by capturing areferencetorunningTotalandamountfrom the surrounding function and using them within its own function body. Capturing by reference ensures thatrunningTotalandamountdo not disappear when the call tomakeIncrementerends, and also ensures thatrunningTotalis available the next time theincrementerfunction is called.

incrementer()函数并没有任何参数,但是在函数体内访问了runningTotal和amount变量。这是因为它从外围函数捕获了runningTotal和amount变量的引用。捕获引用保证了runningTotal和amount变量在调用完makeIncrementer后不会消失,并且保证了在下一次执行incrementer函数时,runningTotal依旧存在。

Note

As an optimization, Swift may instead capture and store acopyof a value if that value is not mutated by a closure, and if the value is not mutated after the closure is created.

为了优化,如果一个值不会被闭包改变,或者在闭包创建后不会改变,Swift 可能会改为捕获并保存一份对值的拷贝。

Swift also handles all memory management involved in disposing of variables when they are no longer needed.

Swift 也会负责被捕获变量的所有内存管理工作,包括释放不再需要的变量。

Here’s an example ofmakeIncrementerin action:

下面是一个使用makeIncrementer的例子:

let incrementByTen = makeIncrementer(forIncrement:10)

This example sets a constant calledincrementByTento refer to an incrementer function that adds10to itsrunningTotalvariable each time it is called. Calling the function multiple times shows this behavior in action:

该例子定义了一个叫做incrementByTen的常量,该常量指向一个每次调用会将其runningTotal变量增加10的incrementer函数。调用这个函数多次可以得到以下结果:

1. incrementByTen()

// returns a value of 10

2. incrementByTen()

// returns a value of 20

3. incrementByTen()

// returns a value of 30

If you create a second incrementer, it will have its own stored reference to a new, separaterunningTotalvariable:

如果你创建了另一个incrementer,它会有属于自己的引用,指向一个全新、独立的runningTotal变量:

let incrementBySeven = makeIncrementer(forIncrement:7)

    incrementBySeven()

// returns a value of 7

Calling the original incrementer (incrementByTen) again continues to increment its ownrunningTotalvariable, and does not affect the variable captured byincrementBySeven:

再次调用原来的incrementByTen会继续增加它自己的runningTotal变量,该变量和incrementBySeven中捕获的变量没有任何联系:

incrementByTen()

// returns a value of 40

Note

If you assign a closure to a property of a class instance, and the closure captures that instance by referring to the instance or its members, you will create a strong reference cycle between the closure and the instance. Swift usescapture liststo break these strong reference cycles. For more information, seeStrong Reference Cycles for Closures.

如果你将闭包赋值给一个类实例的属性,并且该闭包通过访问该实例或其成员而捕获了该实例,你将在闭包和该实例间创建一个循环强引用。Swift 使用捕获列表来打破这种循环强引用。更多信息,请参考闭包引起的循环强引用

Closures Are Reference Types (闭包是引用类型)

In the example above,incrementBySevenandincrementByTenare constants, but the closures these constants refer to are still able to increment therunningTotalvariables that they have captured. This is because functions and closures arereference types.

上面的例子中,incrementBySeven和incrementByTen都是常量,但是这些常量指向的闭包仍然可以增加其捕获的变量的值。这是因为函数和闭包都是引用类型

Whenever you assign a function or a closure to a constant or a variable, you are actually setting that constant or variable to be areferenceto the function or closure. In the example above, it is the choice of closure thatincrementByTenrefers tothat is constant, and not the contents of the closure itself.

无论你将函数或闭包赋值给一个常量还是变量,你实际上都是将常量或变量的值设置为对应函数或闭包的引用。上面的例子中,指向闭包的引用incrementByTen是一个常量,而并非闭包内容本身。

This also means that if you assign a closure to two different constants or variables, both of those constants or variables will refer to the same closure:

这也意味着如果你将闭包赋值给了两个不同的常量或变量,两个值都会指向同一个闭包:

let alsoIncrementByTen = incrementByTen

    alsoIncrementByTen()

// returns a value of 50

Escaping Closures (逃逸闭包)

A closure is said toescapea function when the closure is passed as an argument to the function, but is called after the function returns. When you declare a function that takes a closure as one of its parameters, you can write@escapingbefore the parameter’s type to indicate that the closure is allowed to escape.

当一个闭包作为参数传到一个函数中,但是这个闭包在函数返回之后才被执行,我们称该闭包从函数中逃逸。当你定义接受闭包作为参数的函数时,你可以在参数名之前标注@escaping,用来指明这个闭包是允许“逃逸”出这个函数的。

One way that a closure can escape is by being stored in a variable that is defined outside the function. As an example, many functions that start an asynchronous operation take a closure argument as a completion handler. The function returns after it starts the operation, but the closure isn’t called until the operation is completed—the closure needs to escape, to be called later. For example:

一种能使闭包“逃逸”出函数的方法是,将这个闭包保存在一个函数外部定义的变量中。举个例子,很多启动异步操作的函数接受一个闭包参数作为 completion handler。这类函数会在异步操作开始之后立刻返回,但是闭包直到异步操作结束后才会被调用。在这种情况下,闭包需要“逃逸”出函数,因为闭包需要在函数返回之后被调用。例如:

var completionHandlers: [() ->Void] = []

    func someFunctionWithEscapingClosure(completionHandler: @escaping() ->Void) {

      completionHandlers.append(completionHandler)

}

ThesomeFunctionWithEscapingClosure(_:)function takes a closure as its argument and adds it to an array that’s declared outside the function. If you didn’t mark the parameter of this function with@escaping, you would get a compiler error.

someFunctionWithEscapingClosure(_:)函数接受一个闭包作为参数,该闭包被添加到一个函数外定义的数组中。如果你不将这个参数标记为@escaping,就会得到一个编译错误。

Marking a closure with@escapingmeans you have to refer toselfexplicitly within the closure. For example, in the code below, the closure passed tosomeFunctionWithEscapingClosure(_:)is an escaping closure, which means it needs to refer toselfexplicitly. In contrast, the closure passed tosomeFunctionWithNonescapingClosure(_:)is a nonescaping closure, which means it can refer toselfimplicitly.

将一个闭包标记为@escaping意味着你必须在闭包中显式地引用self。比如说,在下面的代码中,传递到someFunctionWithEscapingClosure(_:)中的闭包是一个逃逸闭包,这意味着它需要显式地引用self。相对的,传递到someFunctionWithNonescapingClosure(_:)中的闭包是一个非逃逸闭包,这意味着它可以隐式引用self。

func someFunctionWithNonescapingClosure(closure: () ->Void) {

    closure()

}

classSomeClass{

  var x=10

  func doSomething() {

      someFunctionWithEscapingClosure{self.x=100}

      someFunctionWithNonescapingClosure{x=200}

       }

}

let instance=SomeClass()

instance.doSomething()

print(instance.x)

// Prints "200"

completionHandlers.first?()

print(instance.x)

// Prints "100"

Autoclosures (自动闭包)

Anautoclosureis a closure that is automatically created to wrap an expression that’s being passed as an argument to a function. It doesn’t take any arguments, and when it’s called, it returns the value of the expression that’s wrapped inside of it. This syntactic convenience lets you omit braces around a function’s parameter by writing a normal expression instead of an explicit closure.

自动闭包是一种自动创建的闭包,用于包装传递给函数作为参数的表达式。这种闭包不接受任何参数,当它被调用的时候,会返回被包装在其中的表达式的值。这种便利语法让你能够省略闭包的花括号,用一个普通的表达式来代替显式的闭包。

It’s common tocallfunctions that take autoclosures, but it’s not common toimplementthat kind of function. For example, theassert(condition:message:file:line:)function takes an autoclosure for itsconditionandmessageparameters; itsconditionparameter is evaluated only in debug builds and itsmessageparameter is evaluated only ifconditionisfalse.

我们经常会调用采用自动闭包的函数,但是很少去实现这样的函数。举个例子来说,assert(condition:message:file:line:)函数接受自动闭包作为它的condition参数和message参数;它的condition参数仅会在 debug 模式下被求值,它的message参数仅当condition参数为false时被计算求值。

An autoclosure lets you delay evaluation, because the code inside isn’t run until you call the closure. Delaying evaluation is useful for code that has side effects or is computationally expensive, because it lets you control when that code is evaluated. The code below shows how a closure delays evaluation.

自动闭包让你能够延迟求值,因为直到你调用这个闭包,代码段才会被执行。延迟求值对于那些有副作用(Side Effect)和高计算成本的代码来说是很有益处的,因为它使得你能控制代码的执行时机。下面的代码展示了闭包如何延时求值。

var customersInLine= ["Chris","Alex","Ewa","Barry","Daniella"]

  print(customersInLine.count)

// Prints "5"

let customerProvider= {customersInLine.remove(at:0) }

print(customersInLine.count)

// Prints "5"

print("Now serving\(customerProvider())!")

// Prints "Now serving Chris!"

print(customersInLine.count)

// Prints "4"

Even though the first element of thecustomersInLinearray is removed by the code inside the closure, the array element isn’t removed until the closure is actually called. If the closure is never called, the expression inside the closure is never evaluated, which means the array element is never removed. Note that the type ofcustomerProvideris notStringbut() -> String—a function with no parameters that returns a string.

尽管在闭包的代码中,customersInLine的第一个元素被移除了,不过在闭包被调用之前,这个元素是不会被移除的。如果这个闭包永远不被调用,那么在闭包里面的表达式将永远不会执行,那意味着列表中的元素永远不会被移除。请注意,customerProvider的类型不是String,而是() -> String,一个没有参数且返回值为String的函数。

You get the same behavior of delayed evaluation when you pass a closure as an argument to a function.

将闭包作为参数传递给函数时,你能获得同样的延时求值行为。

// customersInLine is ["Alex", "Ewa", "Barry", "Daniella"]

func serve(customercustomerProvider: () ->String) {

    print("Now serving\(customerProvider())!")

}

serve(customer: {customersInLine.remove(at:0) } )

// Prints "Now serving Alex!"

Theserve(customer:)function in the listing above takes an explicit closure that returns a customer’s name. The version ofserve(customer:)below performs the same operation but, instead of taking an explicit closure, it takes an autoclosure by marking its parameter’s type with the@autoclosureattribute. Now you can call the function as if it took aStringargument instead of a closure. The argument is automatically converted to a closure, because thecustomerProviderparameter’s type is marked with the@autoclosureattribute.

上面的serve(customer:)函数接受一个返回顾客名字的显式的闭包。下面这个版本的serve(customer:)完成了相同的操作,不过它并没有接受一个显式的闭包,而是通过将参数标记为@autoclosure来接收一个自动闭包。现在你可以将该函数当作接受String类型参数(而非闭包)的函数来调用。customerProvider参数将自动转化为一个闭包,因为该参数被标记了@autoclosure特性。

// customersInLine is ["Ewa", "Barry", "Daniella"]

func serve(customercustomerProvider: @autoclosure() ->String) {

    print("Now serving\(customerProvider())!")

}

serve(customer:customersInLine.remove(at:0))

// Prints "Now serving Ewa!"

Note

Overusing autoclosures can make your code hard to understand. The context and function name should make it clear that evaluation is being deferred.

过度使用autoclosures会让你的代码变得难以理解。上下文和函数名应该能够清晰地表明求值是被延迟执行的。

If you want an autoclosure that is allowed to escape, use both the@autoclosureand@escapingattributes. The@escapingattribute is described above inEscaping Closures.

如果你想让一个自动闭包可以“逃逸”,则应该同时使用@autoclosure和@escaping属性。@escaping属性的讲解见上面的逃逸闭包

// customersInLine is ["Barry", "Daniella"]

var customerProviders: [() ->String] = []

func collectCustomerProviders(_customerProvider: @autoclosure@escaping() ->String) {

    customerProviders.append(customerProvider)

}

collectCustomerProviders(customersInLine.remove(at:0))

collectCustomerProviders(customersInLine.remove(at:0))

print("Collected\(customerProviders.count)closures.")

// Prints "Collected 2 closures."

forc ustomerProviderincustomerProviders{

    print("Now serving\(customerProvider())!")

}

// Prints "Now serving Barry!"

// Prints "Now serving Daniella!"

In the code above, instead of calling the closure passed to it as itscustomerProviderargument, thecollectCustomerProviders(_:)function appends the closure to thecustomerProvidersarray. The array is declared outside the scope of the function, which means the closures in the array can be executed after the function returns. As a result, the value of thecustomerProviderargument must be allowed to escape the function’s scope.

在上面的代码中,collectCustomerProviders(_:)函数并没有调用传入的customerProvider闭包,而是将闭包追加到了customerProviders数组中。这个数组定义在函数作用域范围外,这意味着数组内的闭包能够在函数返回之后被调用。因此,customerProvider参数必须允许“逃逸”出函数作用域。

推荐阅读更多精彩内容

  • 以下翻译自Apple官方文档,结合自己的理解记录下来。翻译基于 swift 3.0.1 原文地址 Closure...
    艺术农阅读 468评论 0 3
  • 闭包 Closures are self-contained blocks of functionality th...
    peterchen阅读 229评论 0 2
  • 人生不易,挚友难得。白驹过隙间,身边只剩下可以彼此寒暄的朋友,真心又何其难寻,能让你捧腹大笑、吐诉真心的老友,各自...
    狐心雪阅读 17评论 0 0
  • 今天复盘总结下半年的第一个月,七月读过的书籍。 一、《聚焦:决定企业的未来》 艾·里斯著 特劳特定位系列的书籍,读...
    王建设自媒体阅读 95评论 0 2
  • 此刻,舒岩正在用他那诗人的眼睛透过劣质镜片望着那辆车,也望着这个畸形的世界。 接下这个生意后,他错过了一次绝佳的机...
    静石阅读 54评论 0 1