diff --git a/source/chapter2/07_Closures.md b/source/chapter2/07_Closures.md index ae7d5235..0abd1ccf 100755 --- a/source/chapter2/07_Closures.md +++ b/source/chapter2/07_Closures.md @@ -374,3 +374,75 @@ let alsoIncrementByTen = incrementByTen alsoIncrementByTen() // 返回的值为50 ``` + +## 自动闭包(Autoclosures) +一个自动闭包,是一个自动被创建的,用于将一个表达式包装为一个参数,进而传递到函数的闭包。当它被调用的时候,它无需任何的参数,然后返回被包装在其中的表达式的值。这种语法糖(syntactic convenience),让你能够用一个普通的表达式,省略围绕在函数参数外的括号,来代替显式的闭包。 +调用一个闭包作为参数的函数是很常见的,不过,实现那样的函数却不常见。举个例子来说,`assert(condition:message:file:line:)`函数,将一个闭包作为它的condition参数和message参数;它的condition参数仅仅在编译时被计算求值,它的message参数仅当Condition参数为false时被计算求值。 +一个自动闭包让你能够延迟计算求值,因为代码段不会被执行直到你调用这个闭包。延迟计算求值对于哪些有副作用(Side Effect)的代码和大量繁杂计算的代码来说是很有益处的,因为它让你控制了代码什么时候被运行。下面的代码展示了一个闭包的延时执行。 + +```swift +var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"] +print(customersInLine.count) +// prints "5" + +let customerProvider = { customersInLine.removeAtIndex(0) } +print(customersInLine.count) +// prints "5" + +print("Now serving \(customerProvider())!") +// prints "Now serving Chris!" +print(customersInLine.count) +// prints "4" +``` + +尽管在闭包的代码中,`customersInLine`的第一个元素被移除了,不过在闭包被调用之前,这个元素是不会被真正移除的。如果这个闭包永远不被调用,那么在闭包里面的表达式将永远不会之行,那意味着列表中的元素永远不会被移除。请注意,`customerProvider`的类型不是一个字符(String),而是一个`() -> String`,一个没有参数而返回值为字符(String)的函数。 + +你当你将一个闭包作为参数传递到函数中,你能够看到相同的延时计算的行为。 + +```swift +// customersInLine is ["Alex", "Ewa", "Barry", "Daniella"] +func serveCustomer(customerProvider: () -> String) { + print("Now serving \(customerProvider())!") +} +serveCustomer( { customersInLine.removeAtIndex(0) } ) +// prints "Now serving Alex!" +``` + +`serveCustomer(_:) ` 需要一个显式的闭包用来返回一个顾客的名字。下面这个版本的`serveCustomer(_:)`完成了相同的操作,不过他通过将一个参数标记为`@autoclosure`特性,来代替一个显式的闭包作为参数。现在你可以使用一个‘以字符为参数的函数’作为参数,而不是用一个‘闭包’作为参数来调用这个函数了。这个被调用的函数参数将自动转化为一个闭包参数,因为`customerProvider`被打上了`@autoclosure`特性标志。 + +```swift +// customersInLine is ["Ewa", "Barry", "Daniella"] +func serveCustomer(@autoclosure customerProvider: () -> String) { + print("Now serving \(customerProvider())!") +} +serveCustomer(customersInLine.removeAtIndex(0)) +// prints "Now serving Ewa!" +``` + +> 注意: +> 过度使用 `autoclosures` 会让你的代码变得难以理解,上下文和函数名应该能够明确的表示出,他是被延迟执行的。 + +`@autoclosure` 特性隐式包括了 `@noescape` 特性, 它意味着闭包仅仅在函数中被使用,这个闭包不允许以被保存起来的方式,让它’逃逸‘出当前的作用域,然后在函数返回之后再被执行,如果你想让这个闭包可以‘逃逸’,则应该使用 `@autoclosure(escaping)`特性. + +```swift +// customersInLine is ["Barry", "Daniella"] +var customerProviders: [() -> String] = [] +func collectCustomerProviders(@autoclosure(escaping) customerProvider: () -> String) { + customerProviders.append(customerProvider) +} +collectCustomerProviders(customersInLine.removeAtIndex(0)) +collectCustomerProviders(customersInLine.removeAtIndex(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!" +``` + +在上面的代码中,代替调用传入的customer参数闭包,`collectCustomerProviders(_:) `函数将闭包追加到了`customerProviders`队列中,这个队列定义在函数作用域范围外,那就意味着,在队列里面的闭包将会在函数返回之后被执行。作为结果,customer参数的值必须允许‘逃逸’出函数作用域。 + +更多关于 `@autoclosure` 和 `@noescape` 特性的信息, 参见 Declaration Attributes. +