当nsviewcontroller在swift下从故事板实例化时,似乎在某个地方有一个引用循环。
多次调用以下代码将实例化并设置新的视图控制器,但旧的视图控制器永远不会解除锁定。在代码中,containerViewController是应包含单个nsviewcontroller的nsviewcontroller,containerViewcontainerViewController中的子视图,identifier是要实例化的情节提要标识符。

// Remove any sub viewcontrollers and their views
for viewController in containerViewController.childViewControllers as [NSViewController] {
    viewController.view.removeFromSuperview()
    viewController.removeFromParentViewController()
}
// Create and set up the new view controller and view.
let viewController = storyboard!.instantiateControllerWithIdentifier(identifier) as NSViewController
let view = viewController.view
view.translatesAutoresizingMaskIntoConstraints = false
containerView.addSubview(viewController.view)
containerViewController.addChildViewController(viewController)
containerView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|[view]|", options: nil, metrics: nil, views: ["view": view]))
containerView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[view]|", options: nil, metrics: nil, views: ["view": view]))

(示例项目不再可用)
我用了一个苹果TSI,他们同意这是一个bug,我已经提交了,但我希望有人已经提出了这一点,现在视为nsviewcontrollers和故事板是事实上的OSX。你是如何解决这个问题的?或者它不会影响到其他人,我做错了什么?
预边界编辑:每个视图控制器必须能够从代码链接到任何其他视图控制器,因为目的地是动态确定的。这似乎可以作为一种选择来消除隔阂。
错误已修复
从Xcode6.3开始,这不再是一个bug。

最佳答案

另一个答案。
似乎只有故事板上定义的段可以执行视图控制器释放。
所以,这里有一个非常难看,但工作的解决方案。

class DismissSegue: NSStoryboardSegue {

    var nextViewControllerIdentifier:String?

    override func perform() {
        let src = self.sourceController as NSViewController
        let windowController = src.view.window!.windowController() as TopLevelWindowController

        src.view.removeFromSuperview()
        src.removeFromParentViewController()

        if let identifier = nextViewControllerIdentifier {
            windowController.setNewViewController(identifier)
        }
    }
}

class TopLevelWindowController: NSWindowController {

    var containerView: NSView!
    var containerViewController: ContainerViewController! {
        didSet {
            setNewViewController("FirstView")
        }
    }

    func setNewViewController(identifier: String) {
        // Create and set up the new view controller and view.
        let viewController = storyboard!.instantiateControllerWithIdentifier(identifier) as NSViewController
        let view = viewController.view
        view.translatesAutoresizingMaskIntoConstraints = false
        containerView.addSubview(viewController.view)
        containerViewController.addChildViewController(viewController)
        containerView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|[view]|", options: nil, metrics: nil, views: ["view": view]))
        containerView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[view]|", options: nil, metrics: nil, views: ["view": view]))
    }
}

class ContainerViewController: NSViewController {

    @IBOutlet var containerView: NSView!

    override func viewDidAppear() {
        super.viewDidAppear()
        if let window = view.window {
            if let topLevelWindowController = window.windowController() as? TopLevelWindowController {
                topLevelWindowController.containerView = containerView
                topLevelWindowController.containerViewController = self
            }
        }
    }

}

class FirstViewController: NSViewController {

    required init?(coder: NSCoder) {
        super.init(coder: coder)
        let pointerAddress = NSString(format: "%p", unsafeBitCast(self, Int.self))
        NSLog("First VC init at \(pointerAddress)")
    }

    deinit {
        let pointerAddress = NSString(format: "%p", unsafeBitCast(self, Int.self))
        NSLog("First VC de-init at \(pointerAddress)")
    }

    override func prepareForSegue(segue: NSStoryboardSegue, sender: AnyObject?) {
        if let segue = segue as? DismissSegue {
            segue.nextViewControllerIdentifier = "SecondView"
        }
    }
}

class SecondViewController: NSViewController {

    required init?(coder: NSCoder) {
        super.init(coder: coder)
        let pointerAddress = NSString(format: "%p", unsafeBitCast(self, Int.self))
        NSLog("Second VC init at \(pointerAddress)")
    }

    deinit {
        let pointerAddress = NSString(format: "%p", unsafeBitCast(self, Int.self))
        NSLog("Second VC de-init at \(pointerAddress)")
    }

    override func prepareForSegue(segue: NSStoryboardSegue, sender: AnyObject?) {
        if let segue = segue as? DismissSegue {
            segue.nextViewControllerIdentifier = "FirstView"
        }
    }
}

修改故事板的步骤:
断开按钮的连接。
使用normal@IBAction创建“虚拟”视图控制器场景。
将自定义段从按钮连接到“虚拟”。
如图所示配置这些segue。
如果这个解决方案不符合您的要求,请告诉我。

关于macos - NSViewController引用周期与 Storyboard,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/26836515/

10-11 21:16