將閉包傳遞給函式
函式可以接受閉包(或其他函式)作為引數:
func foo(value: Double, block: () -> Void) { ... }
func foo(value: Double, block: Int -> Int) { ... }
func foo(value: Double, block: (Int, Int) -> String) { ... }
尾隨閉包語法
如果函式的最後一個引數是閉包,則可以在函式呼叫之後寫入閉包括號 {
/ }
:
foo(3.5, block: { print("Hello") })
foo(3.5) { print("Hello") }
dispatch_async(dispatch_get_main_queue(), {
print("Hello from the main queue")
})
dispatch_async(dispatch_get_main_queue()) {
print("Hello from the main queue")
}
如果函式的唯一引數是閉包,則在使用尾隨閉包語法呼叫它時,也可以省略一對括號 ()
:
func bar(block: () -> Void) { ... }
bar() { print("Hello") }
bar { print("Hello") }
@noescape
引數
標記為 @noescape
的閉包引數保證在函式呼叫返回之前執行,因此在閉包體內不需要使用 self.
:
func executeNow(@noescape block: () -> Void) {
// Since `block` is @noescape, it's illegal to store it to an external variable.
// We can only call it right here.
block()
}
func executeLater(block: () -> Void) {
dispatch_async(dispatch_get_main_queue()) {
// Some time in the future...
block()
}
}
class MyClass {
var x = 0
func showExamples() {
// error: reference to property 'x' in closure requires explicit 'self.' to make capture semantics explicit
executeLater { x = 1 }
executeLater { self.x = 2 } // ok, the closure explicitly captures self
// Here "self." is not required, because executeNow() takes a @noescape block.
executeNow { x = 3 }
// Again, self. is not required, because map() uses @noescape.
[1, 2, 3].map { $0 + x }
}
}
Swift 3 注:
請注意,在 Swift 3 中,你不再將塊標記為 @noescape。現在,塊預設情況下不會轉義。在 Swift 3 中,不是將閉包標記為非轉義,而是使用“@escaping”關鍵字標記一個轉義閉包的函式引數。
throws
和 rethrows
與其他函式一樣,閉包可能會丟擲錯誤 :
func executeNowOrIgnoreError(block: () throws -> Void) {
do {
try block()
} catch {
print("error: \(error)")
}
}
當然,該函式可以將錯誤傳遞給其呼叫者:
func executeNowOrThrow(block: () throws -> Void) throws {
try block()
}
但是,如果傳入的塊沒有丟擲,則呼叫者仍然使用 throw 函式:
// It's annoying that this requires "try", because "print()" can't throw!
try executeNowOrThrow { print("Just printing, no errors here!") }
解決方案是 rethrows
,它指定函式只能在其閉包引數丟擲時丟擲 :
func executeNowOrRethrow(block: () throws -> Void) rethrows {
try block()
}
// "try" is not required here, because the block can't throw an error.
executeNowOrRethrow { print("No errors are thrown from this closure") }
// This block can throw an error, so "try" is required.
try executeNowOrRethrow { throw MyError.Example }
許多標準庫函式使用 rethrows
,包括 map()
,filter()
和 indexOf()
。