簡體   English   中英

呈現視圖“擁有”時,Swift無主自我泄漏

[英]Swift unowned self leaking when 'owned' by a view being presented

據我所知,在不存在泄漏的情況下,我正在經歷一個無主的自我泄漏。 讓我舉一個例子,它有點人為,所以請允許我,我嘗試過盡可能簡單的情況。

假設我有一個簡單的視圖控制器,它對viewDidLoad執行閉包:

class ViewController2: UIViewController {

    var onDidLoad: (() -> Void)?

    override func viewDidLoad() {
        super.viewDidLoad()
        onDidLoad?()
    }
}

還有一個ViewHandler類,它擁有該視圖控制器的實例,並使用一個未擁有的引用將對notify函數的調用注入到其閉包中:

class ViewHandler {

    private let viewController2 = ViewController2()

    func getViewController() -> ViewController2 {
        viewController2.onDidLoad = { [unowned self] in
            self.notify()
        }
        return viewController2
    }

    func notify() {
        print("My viewcontroller has loaded its view!")
    }
}

然后,當它的視圖控制器由另一個視圖控制器呈現時,ViewHandler在漏出時會泄漏:

class ViewController: UIViewController {

    private var viewHandler: ViewHandler?

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        viewHandler = ViewHandler()
        self.present(viewHandler!.getViewController(), animated: true, completion: nil)

        viewHandler = nil // ViewHandler is leaking here.
    }
}

我知道該示例可能看起來有些虛構,但據我所知應該不會泄漏。 讓我嘗試分解一下:

在展示ViewHandler.ViewController2之前,所有權應如下所示:

ViewController -> ViewHandler -> ViewController2 -|
                       ^                          |
                       |_ _ _ _ unowned _ _ _ _ _ |

顯示ViewHandler.ViewController2后,所有權應如下所示:

         _______________________________
        |                               v
ViewController -> ViewHandler -> ViewController2 -|
                       ^                          |
                       |_ _ _ _ unowned _ _ _ _ _ |

在取消ViewHandler之后,所有權應如下所示:

         _______________________________
        |                               v
ViewController    ViewHandler -> ViewController2 -|
                       ^                          |
                       |_ _ _ _ unowned _ _ _ _ _ |

沒有任何東西擁有ViewHandler,應該將其釋放。 但是,事實並非如此,ViewHandler正在泄漏。

如果將注入onDidLoad的閉包的捕獲列表中的引用更改為弱,則不會發生泄漏,並且按預期方式釋放ViewHandler:

func getViewController() -> ViewController2 {
    viewController2.onDidLoad = { [weak self] in
        self?.notify()
    }
    return viewController2
}

另外,我無法解釋的是,如果我保持引用為不擁有並讓ViewHandler從NSObject繼承,則ViewHandler將按預期方式釋放,並且不會泄漏:

class ViewHandler: NSObject {

    private let viewController2 = ViewController2()

    func getViewController() -> ViewController2 {
        viewController2.onDidLoad = { [unowned self] in
            self.notify()
        }
        return viewController2
    }

    ....
}

誰能解釋發生了什么?

根據我目前的理解,符合NSObjectProtocol的NSObject。 這種對象是從具有成熟內存管理功能的Objective-C橋接的。 當您使用class ,我們大多數人仍在使用此類。 如果您是從NSObject構造一個類,則應該不會受到傷害。

swift class的管理似乎有點實驗性的風格,因為人們傾向於盡可能使用structure 因此,有些意外行為並不奇怪。

因此,當您選擇swift class ,您必須根據這種經驗進行更多思考。 但是好的一面是,它們可能會帶來一些與經典NSObject不同的新的穩定功能。

為了簡單起見,只需刪除vc2作為私有變量即可。

class ViewHandler {

func getViewController() -> ViewController2 {

    let viewController2  = ViewController2()

    viewController2.onDidLoad = { [unowned self] in
        self.notify()
    }
    return viewController2
}

func notify() {
    print("My viewcontroller has loaded its view!")
}


}

在這種情況下,泄漏仍然存在。 unowned判斷是很難的條件。 實際上,viewHandler的所有權已轉移到vc2。

釋放vc2后,泄漏也消失了。 這是暫時的泄漏。

  var firstTime: Bool = true

    override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)

      if firstTime{
        viewHandler = ViewHandler()
        let vc = viewHandler!.getViewController()
        self.present(vc, animated: true, completion: nil)

        viewHandler = nil // ViewHandler is leaking here.

           DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
              vc.dismiss(animated: true, completion: nil)
               // leaking is over.
           }
    }
    firstTime.toggle()
}

甚至是特定的,所有權也由vc.onDidLoad占據。 如果

     viewHandler = nil // ViewHandler is leaking here.

要么

     vc.onDidLoad?() // error "ViewHandler has been dealloc!!"

要么

     vc.onDidLoad = nil. // There is no error here. 

因此,您應該在這里處理。 從而解決了“泄漏”問題。

暫無
暫無

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

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