簡體   English   中英

該視圖控制器是否以“ willSet / didSet”對泄漏?

[英]Does this view controller leak in a “willSet/didSet” pair?

您有一個vc(綠色),並且有一個面板(黃色)“持有人”

在此處輸入圖片說明

假設您有十種不同的視圖控制器...價格,銷售,庫存,卡車,駕駛員,調色板,您將一次將它們放置在黃色區域中。 它將從情節提要中動態加載每個VC

 instantiateViewController(withIdentifier: "PricesID") as! Prices

我們將保持當前的VC之一current 這是允許您在它們之間“交換”的代碼...

>>注意,這是錯誤的。 請勿使用此代碼<<

必須做Sulthan在下面解釋的事情。

var current: UIViewController? = nil {
    willSet {
        // recall that the property name ("current") means the "old" one in willSet
        if (current != nil) {
            current!.willMove(toParentViewController: nil)
            current!.view.removeFromSuperview()
            current!.removeFromParentViewController()
            // "!! point X !!"
        }
    }
    didSet {
        // recall that the property name ("current") means the "new" one in didSet
        if (current != nil) {
            current!.willMove(toParentViewController: self)
            holder.addSubview(current!.view)
            current!.view.bindEdgesToSuperview()
            current!.didMove(toParentViewController: self)
        }
    }
}

>>>>>>>>重要!<<<<<<<<<

還要注意,如果您執行這樣的操作,則在完成綠色頁面后,必須擺脫黃色視圖控制器。 否則, current將保留它,並且永遠不會釋放綠色頁面:

override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
    current = nil
    super.dismiss(animated: flag, completion: completion)
}

繼續,您將使用如下所示的current屬性:

func showPrices() {
    current = s.instantiateViewController(withIdentifier: "PricesID") as! Prices
}
func showSales() {
    current = s.instantiateViewController(withIdentifier: "SalesID") as! Sales
}

但是考慮一下,注意“點X” 通常,您必須將要擺脫的視圖控制器設置為nil。

blah this, blah that
blah.removeFromParentViewController()
blah = nil

但是我(不要認為)您可以在“ willSet”代碼塊中真正將current設置為nil。 而且我很欣賞它即將被設置為某種東西(在didSet中)。 但這似乎有些奇怪。 缺少了什么? 您甚至可以在計算屬性中執行此類操作嗎?


最終可用版本.....

使用Sulthan的方法,在經過大量測試后,此方法可以完美地工作。

所以這樣叫

// change yellow area to "Prices"
current = s.instantiateViewController(withIdentifier: "PricesID") as! Prices

// change yellow area to "Stock"
current = s.instantiateViewController(withIdentifier: "StickID") as! Stock

這很好用...

var current: UIViewController? = nil { // ESSENTIAL to nil on dismiss
    didSet {
        guard current != oldValue else { return }

        oldValue?.willMove(toParentViewController: nil)
        if (current != nil) {
            addChildViewController(current!)

            holder.addSubview(current!.view)
            current!.view.bindEdgesToSuperview()
        }
        oldValue?.view.removeFromSuperview()

        oldValue?.removeFromParentViewController()
        if (current != nil) {
            current!.didMove(toParentViewController: self)
        }
    }
    // courtesy http://stackoverflow.com/a/41900263/294884
}
override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
   // ESSENTIAL to nil on dismiss
    current = nil
    super.dismiss(animated: flag, completion: completion)
}

讓我們將問題分為兩個部分:(1)是否存在“泄漏”? (2)這是個好主意嗎?

首先是“泄漏”。 簡短的回答:不。 即使您未將current設置為nil ,它所持有的視圖控制器也顯然不會“泄漏”; 當包含的視圖控制器不存在時, current指向的視圖控制器也將消失。

但是, current視圖控制器的壽命確實比需要的更長。 因此,這似乎很愚蠢。 不需要對子視圖控制器的強大參考current ,因為畢竟它是您的childViewControllers[0] (如果您正確執行了子視圖控制器的“跳舞”)。 因此,您僅使用屬性復制childViewControllers屬性已經執行的操作。

這就引出了第二個問題:您在做一個好主意嗎? 不,我知道您來自哪里,您想封裝子視圖控制器的“舞蹈”。 但是無論如何,您的舞步都不正確。 因此,您正在顛覆視圖控制器層次結構。 為了封裝“舞蹈”,我會說,您最好正確地進行舞蹈並提供執行該舞蹈的功能 ,以及提供了引用childViewController[0]計算只讀屬性(如果存在)。

在這里,我假設我們一次只能擁有一個子視圖控制器; 我認為這樣做要好得多:

var current : UIViewController? {
    if self.childViewControllers.count > 0 {
        return self.childViewControllers[0]
    }
    return nil
}

func removeChild() {
    if let vc = self.current {
        vc.willMove(toParentViewController: nil)
        vc.view.removeFromSuperview()
        vc.removeFromParentViewController()
    }
}

func createChild(_ vc:UIViewController) {
    self.removeChild() // There Can Be Only One
    self.addChildViewController(vc) // *
    // ... get vc's view into the interface ...
    vc.didMove(toParentViewController: self)
}

我不認為使用didSet實際上是錯誤的。 但是,最大的問題是您試圖在willSetdidSet之間拆分代碼,因為willSet didSet 您可以始終在didSet使用oldValue

var current: UIViewController? = nil {
    didSet {
        guard current != oldValue else {
           return
        }

        oldValue?.willMove(toParentViewController: nil)            
        if let current = current {
            self.addChildViewController(current)
        }

        //... add current.view to the view hierarchy here...
        oldValue?.view.removeFromSuperview()

        oldValue?.removeFromParentViewController()
        current?.didMove(toParentViewController: self)
    }
}

順便說一下,函數的調用順序很重要。 因此,我不建議將功能拆分為removeadd 否則,這兩個控制器的viewDidDisappearviewDidAppear的順序可能令人驚訝。

暫無
暫無

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

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