2015-10-21 37 views
12

我在哪裏定義捕獲的Swift嵌套閉包引用?在swift中正確放置捕獲列表嵌套閉包

以將該代碼作爲一個例子:

import Foundation 

class ExampleDataSource { 
    var content: Any? 
    func loadContent() { 
     ContentLoader.loadContentFromSource() { [weak self] loadedContent in 
      // completion handler called on background thread 
      dispatch_async(dispatch_get_main_queue()) { [weak self] in 
       self?.content = loadedContent 
      } 
     } 
    } 
} 

class ContentLoader { 
    class func loadContentFromSource(completion: (loadedContent: Any?) -> Void) { 
     /* 
     Load content from web asynchronously, 
     and call completion handler on background thread. 
     */ 
    } 
} 

在這個例子中,在[weak self]後兩者封被使用,但是如果我省略[weak self]編譯器是完全快樂從尾部封閉件中的任一個。

所以這讓我定義我的捕獲列表3個選項:

  1. 每封嵌套領導到基準
  2. 只在第一封定義捕獲定義捕獲。
  3. 僅在實際使用引用的最嵌套閉包上定義捕獲。

我的問題是:

如果我知道我的ExampleDataSource可能在某些時候是nil,究竟是去與最好的選擇?

回答

14

請務必注意,GCD dispatch_async不會導致保留週期。換句話說,當程序塊完成執行時,GCD將不保留塊內的任何引用。

對於類之間的強引用或分配給實例屬性的閉包中的強引用,情況也是如此。 Apple Documentation

這就是說,在這個例子中,正確答案是選項2,只在第一個閉包上定義捕獲。

出於測試目的,我修改了代碼略:

class ExampleDataSource { 
    init() { 
     print("init()") 
    } 
    deinit { 
     print("deinit") 
    } 
    var content: Any? 
    func loadContent() { 
     print("loadContent()") 
     ContentLoader.loadContentFromSource() { [weak self] loadedContent in 
      dispatch_async(dispatch_get_main_queue()) { 
       print("loadedContent") 
       self?.content = loadedContent 
      } 
     } 
    } 
} 

class ContentLoader { 
    class func loadContentFromSource(completion: (loadedContent: Any?) -> Void) { 
     dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0)) { 
      sleep(5) // thread will hang for 5 seconds 
      completion(loadedContent: "some data") 
     } 
    } 
} 

首先我創建,var myDataSource: ExampleDataSource? = ExampleDataSource()。我們運行myDataSource.loadContent()

在完成處理程序有機會運行之前,我設置myDataSource = nil,刪除對它的所有引用。

調試控制檯指示自行參考未被採納:

init() 
loadContent() 
deinit 
loadedContent 

看起來我們找到了我們答案!但是,僅僅完成的緣故,讓我們來測試的替代品......

如果[weak self]改爲只在內心最尾隨封閉拍攝,GCD將保留ExampleDataSource直到塊已執行完畢,這解釋了爲什麼調試反而會看起來像這樣的:

init() 
loadContent() 
loadedContent 
deinit 

如果不包括捕獲列表會發生同樣的事情,我們從來沒有可選解開self,雖然編譯器,並試圖警告你!

雖然在所有尾隨閉包中包含[weak self]捕獲在技術上並不正確,但它確實會降低代碼的可讀性,並且不會感覺到「Swift-like」。