swift - 这个 View Controller 是否在 "willSet/didSet"对中泄漏?

标签 swift memory-leaks uiviewcontroller

你有一个 vc(绿色),它有一个面板(黄色)“holder”

enter image description here

假设您有十个不同的 View Controller ...价格、销售、库存、卡车、司机、调色板,您将把它们一次一个地放在黄色区域。它会从 Storyboard中动态加载每个 VC

 instantiateViewController(withIdentifier: "PricesID") as! Prices

我们将在 current 中保留当前的 ​​VC。这是允许您在它们之间“交换”的代码...

>>注意,这是错误的。不要使用此代码<<

必须按照 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)
        }
    }
}

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

另请注意,如果您执行此类操作,则必须在完成绿色页面后摆脱黄色 View Controller 。否则 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 点”。通常,您一定要将要删除的 View Controller 设置为 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,它持有的view controller显然不会“泄露”;当包含的 View Controller 不复存在时,current 指向的 View Controller 也不复存在。

但是,current view controller 的生命周期比它需要的要长。因此,这似乎是一件愚蠢的事情。不需要对 subview Controller 的强引用current,因为它毕竟是你的childViewControllers[0](如果你做 subview Controller “正确地跳舞)。因此,您只是用您的属性复制 childViewControllers 属性已经所做的事情。

那么这就引出了第二个问题:您的做法是个好主意吗?不,我知道你来自哪里——你想封装 subview Controller 的“舞蹈”。但是无论如何你都在错误地跳舞;你因此颠覆了 View Controller 层次结构。为了封装“舞蹈”,我会说你最好正确地完成舞蹈并提供执行它的函数,以及一个计算的只读属性,它引用到 childViewController[0](如果存在)。

在这里,我假设我们一次只有一个 subview Controller ;我认为这比您尝试做的事情要好得多:

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)
}

关于swift - 这个 View Controller 是否在 "willSet/didSet"对中泄漏?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41898476/

相关文章:

ios - Swift 3 检查使用了哪个转场

swift - 将 Sprite 宽度缩放到屏幕宽度的问题

ios - 即使运行 HelloWorld_IPhone monotouch,仪器中的对象也会泄漏

android - 我如何知道在任何给定时刻我有多少堆内存?

tomcat - quartz : Memory Leak?

ios - 对于 iOS,有什么方法可以知道导航源?

uitableview - Swift:无法从 UIViewController 转到 UITableViewController

ios - 在 XCUITest 的可访问性指示器中找不到堆栈 View

ios - setNavigationBarHidden 不适用于其他类(Swift 3.0)

ios - 导航 Controller 和内存值