簡體   English   中英

在swift中嵌套閉包中正確放置捕獲列表

[英]Correct placement of capture list in nested closures in 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 ,那么最好的選擇是什么?

值得注意的是,GCD dispatch_async不會導致保留周期。 換句話說,當塊完成執行時,GCD將不保留塊內的任何引用。

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

話雖如此,在這個例子中,正確答案是選項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() var myDataSource: ExampleDataSource? = ExampleDataSource()

然后我運行myDataSource.loadContent()

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

調試控制台指示未保留對self的引用:

init()
loadContent()
deinit
loadedContent

看起來我們找到了答案! 但為了完成,讓我們測試替代方案......

如果僅在最內部的尾隨閉包上捕獲[weak self] ,則GCD將保留ExampleDataSource直到塊完成執行,這解釋了為什么調試將如下所示:

init()
loadContent()
loadedContent
deinit

如果沒有包含捕獲列表,我們也永遠不會打開self ,雖然編譯器確實試圖警告你,但同樣的事情會發生!

雖然在所有尾隨閉包中包含[weak self]捕獲在技術上不正確,但它確實減損了代碼的可讀性,並且感覺不像'類似Swift'。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM