NSViewController,当从Swift下的故事板实例化时,似乎在某个地方有一个参考周期。
多次调用以下代码将实例化并设置新的视图控制器,但旧的视图控制器永远不会被释放。在代码中,containerViewController
是一个NSViewController,它应该包含一个NSViewController,containerView
是containerViewController
中的子视图,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]))
(示例项目不再可用)
我使用了Apple TSI并且他们认为这是我提交的一个错误,但是我希望其他人能够反对这一点,因为现在看来NSViewControllers和故事板现在是OSX上的事实。你是如何解决这个问题的?或者它不影响其他人,我做错了什么?
Pre-bounty edit:每个视图控制器必须能够从代码链接到任何其他视图控制器,因为目标是即时确定的。这似乎将segues作为一种选择。
错误修复
从Xcode 6.3开始,这不再是一个错误。
答案 0 :(得分:3)
另一个答案。
看来,只有在Storyboard 上定义的 segues才能执行视图控制器释放。 所以,这是一个非常丑陋但有效的解决方法。
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"
}
}
}
修改故事板的步骤:
@IBAction
。NSViewController
的控制器场景。如果此解决方案无法满足您的需求,请与我们联系。
答案 1 :(得分:2)
这可能无法解决您的问题,但我找到的唯一解决方法是:
@IBAction
NSStoryboardSegue
在视图控制器之间切换。这样的事情:
import Cocoa
class TopLevelWindowController: NSWindowController {
}
class ContainerViewController: NSViewController {
}
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)")
}
}
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)")
}
}
class MySegue: NSStoryboardSegue {
override func perform() {
let source = self.sourceController as NSViewController
let destination = self.destinationController as NSViewController
if let containerViewController = source.parentViewController {
source.view.removeFromSuperview()
source.removeFromParentViewController()
let view = destination.view
let containerView = containerViewController.view
view.translatesAutoresizingMaskIntoConstraints = false
containerView.addSubview(view)
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]))
containerViewController.addChildViewController(destination)
}
}
}