簡體   English   中英

Swift - 保持關閉參考泄漏

[英]Swift - Holding closure reference leaks

為什么持有對閉包的引用會導致 memory 泄漏,即使閉包不保留任何內容?

(這始於SwiftUI問題,但我不確定它是否真的相關)

所以這里有一個簡單的 Manager ( ViewModel ),它包含一個閉包。

class Manager: ObservableObject {
    
    private var handler: (() -> Void)?
    
    deinit {
        print("Manager deallocated")
    }
    
    func shouldDismiss(completion: @escaping () -> Void) {
        handler = completion
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
            self.handler?()
        }
    }
}

這是一個視圖,使用它:

struct LeakingView: View {
    
    @ObservedObject var manager: Manager
    
    let shouldDismiss: () -> Void
    
    var body: some View {
        Button("Dismiss") { [weak manager] in
            manager?.shouldDismiss {
                shouldDismiss()
            }
        }
    }
}

這是一個激活流程的ViewController

class ViewController: UIViewController {

    var popup: UIViewController?
    
    @IBAction func openViewAction(_ sender: UIButton) {
        
        let manager = Manager()
        
        let suiView = LeakingView(manager: manager) { [weak self] in
            self?.popup?.view.removeFromSuperview()
            self?.popup?.dismiss(animated: true)
            self?.popup = nil
        }
        
        popup = LKHostingView(rootView: suiView)
        
        
        popup?.view.frame.size = CGSize(width: 300, height: 300)
        view.addSubview(popup!.view)
    }
    
}

運行時,只要 manager 持有閉包,它就會泄漏,並且Manager不會被釋放。
保留周期在哪里?

為什么持有對閉包的引用會導致 memory 泄漏,即使閉包不保留任何內容?

因為閉包確實保留了一些東西。 它保留self

例如,當你說

        manager?.shouldDismiss {
            shouldDismiss()
        }

第二個shouldDismiss表示self.shouldDismiss並保留self 因此,LeakingView 保留了 Manager,但 Manager 通過關閉,現在保留了 LeakingView。 保留周期

這就是為什么人們在此類關閉開始時會說[weak self] in

我建議您可能不想在這里使用[weak self]模式。 您正在定義應該在視圖被關閉后 ½ 秒發生的事情。 因此,您可能希望保留該強參考,直到所需的操作完成。

一種方法是確保Manager在調用處理程序后簡單地刪除其強引用:

class Manager: ObservableObject {
    private var handler: (() -> Void)?
    
    func shouldDismiss(completion: @escaping () -> Void) {
        handler = completion
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
            self.handler?()
            self.handler = nil
        }
    }
}

或者,如果此完成處理程序的使用確實僅限於此方法,您可以將其簡化為:

class Manager: ObservableObject {
    func shouldDismiss(completion: @escaping () -> Void) {
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
            completion()
        }
    }
}

或者

func shouldDismiss(completion: @escaping () -> Void) {
    DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: completion)
}

所有這些方法都將確保Manager不會保留任何超出必要時間的強引用。

現在,調用者可以選擇使用[weak self]如果它願意(即,如果它不希望捕獲的引用甚至存活 ½ 秒),但如果你真的想要“做某事之后它是解雇”,它可能也不會使用weak引用。 但無論哪種方式,它都應該取決於調用者,而不是經理。 經理應該確保它不會在不再需要的情況下繼續關閉。

暫無
暫無

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

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