Swift Closures(閉包)

以下翻譯自Apple官方文檔,結合自己的理解記錄下來。翻譯基于 swift 3.0.1

原文地址

Closures are 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.

閉包是一個單獨的功能性代碼塊,可以在你的代碼中傳遞和使用。

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

閉包可以捕獲并且存儲任意的變量和常量,前提是這些變量和常量是在閉包定義的上下文中。這個被叫做closing over這些變量和常量。swift為你負責處理所有為捕獲常量和變量的內存管理。

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

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

全局和嵌套函數事實上是閉包的特殊情況,閉包有以下三種形式:

  • Global functions are closures that have a name and do not capture any values.
    全局函數:閉包有名字并且不捕獲任何變量和常量的情況。這里的全局函數其實就是我們正常定義的函數。
  • Nested functions are closures that have a name and can capture values from their enclosing function.
    嵌套函數:閉包有名字并且從其被包含的函數內部捕獲變量和常量的情況。
  • 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的閉包表達式很簡潔的風格,并且優化的很好,有著簡單、整齊的語法,這些優化包括:

  • Inferring parameter and return value types from context
    可以從上下文推導參數和返回值類型
  • Implicit returns from single-expression closures
    可以從一個單一表達式隱式的返回
  • Shorthand argument names
    簡約表達參數的名稱
  • Trailing closure syntax
    后續的閉包語法(不明白,后面看具體解釋)

Closure Expressions

Nested functions, as introduced in Nested 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 expressions are 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 the sorted(by:) method over several iterations, each of which expresses the same functionality in a more succinct way.

閉包優化的很好,可以達到使用函數同樣的目的。下面使用sorted方法排序的例子說明閉包的優化是多好,一個優化比一個優化帶勁。

The Sorted Method
Swift’s standard library provides a method called sorted(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, the sorted(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 the sorted(by:) method.

swift標準庫提供了方法sorted(by:)來對數組進行排序,不過需要你提供排序的閉包。sorted(by:)返回一個新的排序后的數組,不對原來的數組做修改。

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

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

The sorted(by:) method accepts a closure that takes two arguments of the same type as the array’s contents, and returns a Bool value to say whether the first value should appear before or after the second value once the values are sorted. The sorting closure needs to return true if the first value should appear before the second value, and false otherwise.

這里解釋排序的規則,返回true表明第一個值要排在后一個值前面,反之亦然。

This example is sorting an array of String values, and so the sorting closure needs to be a function of type (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 the sorted(by:) method:

直接提供函數是閉包的一種方式。像下面這樣。

func backward(_ s1: String, _ s2: String) -> Bool {
 return s1 > 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), the backward(::) function will return true, indicating that s1 should appear before s2 in 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.

羅里吧嗦下規則

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.

說下上面直接提供函數的方式不好,太冗長了。看人家下面是怎么做的。

Closure Expression Syntax

閉包表達式的語法

Closure expression syntax has the following general form:
{ (parameters) -> return type in
 statements
}

The parameters in 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 the backward(::) function from earlier:
閉包版本的backward方法

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

Note that the declaration of parameters and return type for this inline closure is identical to the declaration from the backward(::) 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 written inside the curly braces, not outside of them.

表面看來形式和函數差不多,但是我們的閉包是寫在花括號里面的。

The start of the closure’s body is introduced by the in keyword. 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) -> Bool in return s1 > s2 } )

This illustrates that the overall call to the sorted(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.

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. The sorted(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) and Bool types 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:

因為負責排序的閉包是作為參數傳遞給函數的,所以swift可以推導其參數類型和返回值。

reversedNames = names.sorted(by: { s1, s2 in return s1 > 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 the sorted(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 with String values, because it is assisting with the sorting of an array of strings.

盡管如此,但是仍推薦你不要省略參數和返回值,不然你的代碼閱讀起來會模糊不清。(⊙o⊙)嗯

Implicit Returns from Single-Expression Closures

在明確函數的返回值的情況下,不用再閉包中指明返回值。

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

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

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

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.

簡約的參數名稱。

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. The in keyword can also be omitted, because the closure expression is made up entirely of its body:

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

Here, $0 and $1 refer to the closure’s first and second String arguments.

Operator Methods

There’s actually an even shorter way to write the closure expression above. Swift’s String type defines its string-specific implementation of the greater-than operator (>) as a method that has two parameters of type String, and returns a value of type Bool. This exactly matches the method type needed by the sorted(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:

reversedNames = names.sorted(by: >)

For more about operator method, see Operator 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 a trailing closure instead. 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.

當你的閉包作為函數的最后一個參數時,如果閉包的表達很長,那么你可以使用后續的閉包這種方式來實現。就是在傳參時傳個閉包表達式的名字,后面再實現這個閉包的body,然后函數被調用的時候可以不需要寫閉包表達式的名字。

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 the Closure Expression Syntax section above can be written outside of the sorted(by:) method’s parentheses as a trailing closure:

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’s Array type has a map(_:) 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.

舉個map的例子

After applying the provided closure to each array element, the map(_:) 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 the map(_:) method with a trailing closure to convert an array of Int values into an array of String values. The array [16, 58, 510] is used to create the new array ["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 the numbers array to create an array of String values, by passing a closure expression to the array’s map(_:) method as a trailing closure:

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
}
// strings is inferred to be of type [String]
// its value is ["OneSix", "FiveEight", "FiveOneZero"]

The map(_:) 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.

不用指明number的類型,因為有自動推導。

介紹下map的功能。

In this example, the variable number is initialized with the value of the closure’s number parameter, 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 of String, to indicate the type that will be stored in the mapped output array.

為什么number使用var類型,因為我們要在閉包內修改它,而函數和閉包的參數通常都是常量。

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

解釋下這里閉包的功能

NOTE
The call to the digitNames dictionary’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 that number % 10 will always be a valid subscript key for the digitNames dictionary, and so an exclamation mark is used to force-unwrap the String value stored in the subscript’s optional return value.

解釋下為什么使用digitNames[number % 10]!,強制解析,因為肯定有值啊,這樣更有效率。

The string retrieved from the digitNames dictionary is added to the front of output, effectively building a string version of the number in reverse. (The expression number % 10 gives a value of 6 for 16, 8 for 58, and 0 for 510.)

The number variable is then divided by 10. Because it is an integer, it is rounded down during the division, so 16 becomes 1, 58 becomes 5, and 510 becomes 51.

The process is repeated until number is equal to 0, at which point the output string is returned by the closure, and is added to the output array by the map(_:) method.

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 the map(_:) method’s outer parentheses.

Capturing Values(獲取值)

A closure can capture constants 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.

定義,和OC block差不多,就是在定義的那瞬間截取變量和常量的值,不管后面上下文怎么修改,或者值都不存在了,在閉包內仍然可以使用這些值。

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.

典型的獲取值就是嵌套函數。可以獲取外層函數的參數,也可以獲取外層函數內部定義的變量。

Here’s an example of a function called makeIncrementer, which contains a nested function called incrementer. The nested incrementer() function captures two values, runningTotal and amount, from its surrounding context. After capturing these values, incrementer is returned by makeIncrementer as a closure that increments runningTotal by amount each time it is called.

下面舉個例子,函數作為返回值

func makeIncrementer(forIncrement amount: Int) -> () -> Int {
 var runningTotal = 0
 func incrementer() -> Int {
 runningTotal += amount
 return runningTotal
 }
 return incrementer
}

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

The makeIncrementer(forIncrement:) function defines an integer variable called runningTotal, to store the current running total of the incrementer that will be returned. This variable is initialized with a value of 0.

The makeIncrementer(forIncrement:) function has a single Int parameter with an argument label of forIncrement, and a parameter name of amount. The argument value passed to this parameter specifies how much runningTotal should be incremented by each time the returned incrementer function is called. The makeIncrementer function defines a nested function called incrementer, which performs the actual incrementing. This function simply adds amount to runningTotal, and returns the result.

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

func incrementer() -> Int {
 runningTotal += amount
 return runningTotal
}

The incrementer() function doesn’t have any parameters, and yet it refers to runningTotal and amount from within its function body. It does this by capturing a reference to runningTotal and amount from the surrounding function and using them within its own function body. Capturing by reference ensures that runningTotal and amount do not disappear when the call to makeIncrementer ends, and also ensures that runningTotal is available the next time the incrementer function is called.

閉包會通過reference(引用)的方式訪問上下文中的變量,這樣能夠保證這些變量在外層函數調用結束后不會消失(自然我們會想到block的循環引用問題,就是這么來的),因此,下次我們仍然可以在下次外層函數被調用的時候訪問到這些變量。

NOTE
As an optimization, Swift may instead capture and store a copy of a value if that value is not mutated by a closure, and if the value is not mutated after the closure is created.
Swift also handles all memory management involved in disposing of variables when they are no longer needed.

作為優化,swift在閉包中可能會替換引用的方式,轉而使用存儲一份拷貝的方式,前提是如果這些值不會被閉包改變并且不會在閉包創建后被修改。

Here’s an example of makeIncrementer in action:

下面看例子

let incrementByTen = makeIncrementer(forIncrement: 10)

This example sets a constant called incrementByTen to refer to an incrementer function that adds 10 to its runningTotal variable each time it is called. Calling the function multiple times shows this behavior in action:

incrementByTen()
// returns a value of 10
incrementByTen()
// returns a value of 20
incrementByTen()
// returns a value of 30

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

let incrementBySeven = makeIncrementer(forIncrement: 7)
incrementBySeven()
// returns a value of 7

Calling the original incrementer (incrementByTen) again continues to increment its own runningTotalvariable, and does not affect the variable captured by 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 uses capture lists to break these strong reference cycles. For more information, see Strong Reference Cycles for Closures.

這里就說到了將閉包賦值給一個類的實例屬性時,并且閉包通過索引的方式訪問實例本身或者實例的成員變量,你將會在閉包和實例之間創建一個強引用循環。swift用閉包列表來打破這個循環引用。(后面看看閉包列表是啥)

Closures Are Reference Types

In the example above, incrementBySeven and incrementByTen are constants, but the closures these constants refer to are still able to increment the runningTotal variables that they have captured. This is because functions and closures are reference types.

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

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 to escape a 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 @escaping before the parameter’s type to indicate that the closure is allowed to escape.

這里Escaping翻譯成脫離吧,如果一個閉包作為一個函數的參數,但是閉包的調用是在函數執行完也就是return以后,那么我們稱這個閉包脫離了這個函數。當我們傳遞一個閉包作為函數的參數時,可以在參數的類型后加上@escaping來指明這個閉包可以被允許脫離函數。在swift12版本,閉包默認是都可以脫離的,但是在現在這個swift3版本,閉包默認是不可以脫離的。

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:

脫離函數的一種方式就是將閉包存儲在外層函數之外的一個變量內。還有一個就是異步執行。
例如:網絡請求完成后的回調。

這里文檔舉了一個第一個類型的脫離情況,也就是將閉包存儲在外層函數之外的一個變量內的情況。

var completionHandlers: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
 completionHandlers.append(completionHandler)
}

The someFunctionWithEscapingClosure(_:) 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.

這里必須將completionHandler這個參數指明為@escaping類型的,因為在函數內部,它是被加到一個數組中的,而這個數組中的閉包何時執行,誰也不知道,所以它是脫離函數類型的閉包。

Marking a closure with @escaping means you have to refer to self explicitly within the closure. For example, in the code below, the closure passed to someFunctionWithEscapingClosure(:) is an escaping closure, which means it needs to refer to self explicitly. In contrast, the closure passed to someFunctionWithNonescapingClosure(:) is a nonescaping closure, which means it can refer to selfimplicitly.

還有一個重要的地方就是,脫離函數的閉包內部必須使用self來訪問外層的變量,因為在函數執行完以后,閉包是訪問不到這些變量的。必須在定義這些閉包的地方就將外部的變量值截取到閉包的內部(閉包的特性)。而非脫離性質的閉包就不需要使用self來訪問。

func someFunctionWithNonescapingClosure(closure: () -> Void) {
 closure()
}
 
class SomeClass {
 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

An autoclosure is 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 to call functions that take autoclosures, but it’s not common to implement that kind of function. For example, the assert(condition:message:file:line:) function takes an autoclosure for its condition and message parameters; its condition parameter is evaluated only in debug builds and its message parameter is evaluated only if condition is false.

而通常情況下,我們只調用參數類型是自動閉包的函數,而不會去實現這種函數。例如:assert函數。

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.

自動閉包幫助我們實現延時調用,因為什么時候我們調用這個閉包,什么時候這個閉包才會進行解析,返回當初壓縮的表達式。下面是個延時的例子

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 the customersInLine array 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 of customerProvider is not String but () -> String—a function with no parameters that returns a string.

customerProvider不是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(customer customerProvider: () -> String) {
 print("Now serving \(customerProvider())!")
}
serve(customer: { customersInLine.remove(at: 0) } )
// Prints "Now serving Alex!"

上面是不帶自動閉包類型的函數調用

The serve(customer:) function in the listing above takes an explicit closure that returns a customer’s name. The version of serve(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 @autoclosure attribute. Now you can call the function as if it took a String argument instead of a closure. The argument is automatically converted to a closure, because the customerProvider parameter’s type is marked with the @autoclosure attribute.

下面是帶自動閉包類型的函數調用

// customersInLine is ["Ewa", "Barry", "Daniella"]
func serve(customer customerProvider: @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.

不要過度使用自動閉包,否則會導致你的代碼難以理解。如果有延時調用的情況發生,那么在上下文或者函數的名稱上要體現出來。

If you want an autoclosure that is allowed to escape, use both the @autoclosure and @escaping attributes. The @escaping attribute is described above in Escaping Closures.

最后舉個自動閉包和脫離閉包的混合例子

// 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."
for customerProvider in customerProviders {
 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 its customerProvider argument, the collectCustomerProviders(_:) function appends the closure to the customerProviders array. 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 the customerProvider argument must be allowed to escape the function’s scope.

總結:閉包概念還是比較簡單的,只是表現形式較多,需要多實踐掌握!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,565評論 6 539
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,115評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,577評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,514評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,234評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,621評論 1 326
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,641評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,822評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,380評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,128評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,319評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,879評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,548評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,970評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,229評論 1 291
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,048評論 3 397
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,285評論 2 376

推薦閱讀更多精彩內容