簡體   English   中英

奇怪的弱自我和保留周期行為

[英]Weird weak self and retain cycle behaviour

讓我們考慮以下代碼:

// Just for easier testing
protocol Printer {
    var delayer: Delayer { get }
}

// Retain cycle
class Printer1: Printer {
    private func action() {
        print("action")
    }
    
    private(set) lazy var delayer: Delayer = {
        return Delayer(action)
    }()
    
    deinit {
        print("deinit")
    }
}

// Works fine, but weak mess
class Printer2: Printer {
    private func action() {
        print("action")
    }
    
    private(set) lazy var delayer: Delayer = {
        return Delayer { [weak self] in self?.action() }
    }()
    
    deinit {
        print("deinit")
    }
}

// Questionable hack, but works fine
class Printer3: Printer {
    private func action() {
        print("action")
    }
    
    private(set) lazy var delayer: Delayer = {
        return Delayer(weakAction)
    }()

    // computed property or function is also fine here
    private lazy var weakAction: () -> Void = {
        return { [weak self] in
            self?.action()
        }
    }()
    
    deinit {
        print("deinit")
    }
}

// Retain cycle
class Printer4: Printer {
    private func action() {
        print("action")
    }
    
    private(set) lazy var delayer: Delayer = {
        weak var welf: Printer4? = self
        return Delayer(welf?.action ?? {})
    }()
    
    deinit {
        print("deinit")
    }
}

// Works fine
class Printer5: Printer {
    private func action() {
        print("action")
    }
    
    private(set) lazy var delayer: Delayer = {
        weak var welf: Printer5? = self
        return Delayer { welf?.action() }
    }()
    
    deinit {
        print("deinit")
    }
}

class Delayer {
    private var action: () -> Void
    
    init(_ action: @escaping () -> Void) {
        self.action = action
    }
    
    func run() {
        DispatchQueue.main.asyncAfter(deadline: .now() + 3) { [weak self] in
            self?.action()
        }
    }
}

所以我們有一個打印機 class,它包含一個延遲器 class,它對打印機執行操作並延遲執行。

我們這樣稱呼它:

var printer: Printer? = PrinterX()

printer?.delayer.run()

DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
     printer = nil
}

很清楚為什么 Printer1 創建保留循環。 Action 被傳遞到具有隱式強自我的 delayer 中,由於 Delayer 歸 Printer 所有,因此無法釋放。

Printer2 是我認為的預期方式。 顯然不會創建保留循環,但一直寫起來有點亂。 這就是為什么我開始嘗試其他解決方案的原因。

我不明白為什么 Printer3 不創建保留周期。 因為weakAction是自身的屬性,所以像這樣將它傳遞給 Delayer 應該像在 Printer1 中一樣創建強引用。

我也不明白為什么 Priner4 會創建保留周期。 welf是對 self 的局部弱引用,因此在將其傳遞給 Delayer 時不應增加引用計數。

奇怪的是,在 Printer5 中使用welf inside 閉包不會創建保留循環。

問題

  1. 誰能向我解釋一下 Printer3、Printer4 和 Printer5 上的這種奇怪行為
  2. 我很想使用 Printer3 解決方案。 使用安全嗎? 因為它看起來幾乎像一個錯誤,我可以使用它而不用擔心它會在未來的版本中被修復並因此在我的應用程序中創建保留周期嗎?

首先,所有打印機都在創建並保留自己的 Delayer。 延遲器獲取一個閉包,然后保留該閉包。

讓我們試着一一過一遍。

打印機1

正如您自己所說,很清楚為什么要創建保留周期。 您將self.action實例方法作為閉包傳遞給延遲器,並且由於所有閉包都是引用類型,因此傳遞self.action將保留其周圍的 scope(即 Printer1)。

打印機2

同樣,這里很明顯。 您在傳遞給 Delayer 的閉包中明確地捕獲了對 self 的弱引用,因此沒有創建保留循環。

打印機3

在這里,沒有創建循環保留,因為self.weakAction屬性被立即調用,其結果(一個包含對 self 的弱引用的閉包)被傳遞給 Delayer。 這實際上與Printer2中發生的事情完全相同。

打印機4

首先,您捕獲對 self 的弱引用,然后獲取welf?.action並將結果傳遞給 Delayer。 同樣,立即調用welf?.action ,並將結果(指向實例方法的指針)傳遞給 Delayer。 對 self 的弱引用僅在周圍 scope(惰性 var 創建范圍)的持續時間內保留,傳遞action實例方法將保留 self。 這與Printer1相同。

打印機5

在這里,您首先創建對 self 的弱引用,然后在傳遞給 Delayer 的新閉包中捕獲該弱引用。 由於傳遞的閉包中從未直接引用過self ,因此它不會捕獲 scope 中的self ,只會捕獲welf弱引用。 這與Printer2幾乎相同,但語法略有不同。

就個人而言,我會選擇Printer2方式(創建一個新的閉包,保留對 self 的弱引用並使用它來調用self?.action )。 它使代碼最容易遵循(而不是保留一個帶有弱捕獲自我的閉包的變量)。 但是,根據您的實際用例,它當然可能有意義。

暫無
暫無

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

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