2016-09-15 139 views
32

我是Swift的新手,當我遇到轉義關閉時,我正在閱讀手冊。我沒有得到手冊的描述。有人能夠簡單地向我解釋Swift中的轉義閉包嗎?Swift中的轉義關閉

+3

引用手冊,「當閉包被作爲參數傳遞給函數,但在函數返回後被調用時,閉包被認爲是轉義函數。」因此,如果閉包被同步調用,非轉義。一個例子可能是枚舉閉包,或者'map','filter'等功能方法。如果它是異步調用的(即,稍後),它將被轉義。轉義閉包最常見的例子是一些緩慢異步任務的完成處理程序,如網絡請求。 – Rob

+0

如果您認爲我的答案回答了您的問題,請考慮點擊該複選標記來接受。 – Sweeper

回答

61

考慮這個類:

class A { 
    var closure: (() -> Void)? 
    func someMethod(closure:() -> Void) { 
     self.closure = closure 
    } 
} 

someMethod分配傳入的關閉,在類的屬性。

現在,這裏是另一個類:

class B { 
    var number = 0 
    var a: A = A() 
    func anotherMethod() { 
     a.someMethod { self.number = 10 } 
    } 
} 

如果我打電話anotherMethod,關閉{ self.number = 10 }將被存儲在A實例。由於self在關閉中被捕獲,因此A的實例也將對其進行強有力的引用。

這基本上是一個逃脫關閉的例子!

你可能想知道,「什麼?那麼封閉從哪裏逃出來?

閉包從方法的範圍轉移到類的範圍。它可以稍後調用,即使在另一個線程上!如果處理不當,這可能會導致問題。

爲了避免意外逃脫倒閉,造成保留週期和其它問題,請使用@noescape屬性:

class A { 
    var closure: (() -> Void)? 
    func someMethod(@noescape closure:() -> Void) { 
    } 
} 

現在,如果你嘗試寫self.closure = closure,它不編譯!

更新:

在斯威夫特3,關閉所有參數無法在默認情況下逃生。您必須添加@escaping屬性才能使閉包能夠從當前範圍轉義。這爲您的代碼增加了更多的安全性!

class A { 
    var closure: (() -> Void)? 
    func someMethod(closure: @escaping() -> Void) { 
    } 
} 
+0

非常感謝。這真的有幫助。 –

+0

如果您認爲我的回答可以回答您的問題,請考慮點擊該複選標記來接受它@NikhilSridhar – Sweeper

+1

對此答案的讚美!用簡單的方式解釋一個簡單的答案。 –

27

我正在採取更簡單的方式。

考慮這個例子:

func testFunctionWithNonescapingClosure(closure:() -> Void) { 
     closure() 
} 

上面是一個非閉合逸出,因爲封閉件是 方法返回之前調用。

考慮相同的例子與asynchoronous操作:

func testFunctionWithEscapingClosure(closure:@escaping() -> Void) { 
     DispatchQueue.main.async { 
      closure() 
     } 
} 

上面的例子包含一個逸出閉合因爲由於異步操作的函數返回之後的閉合調用可能發生。

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

在上述情況中可以很容易地實現封閉件外側移動功能的 體所以它需要有一個逸出閉合。

逃離並添加編譯器優化斯威夫特3.您可以搜索nonescaping封閉的優勢不逃避關閉。

+0

這也是一個很好的做法,可以給這個好的答案增加非易失性閉包的優點,以及可以在需要時使用的示例。 –