PresentedViewControllers memory leak

时间:2015-07-31 20:44:30

标签: macos swift memory-leaks xcode6 viewcontroller

I have a single window utility app for OS X written using storyboards in Swift, Xcode 6. I have some helper windows which are presented modally with segues. These are dismissed using dismissViewController(self). However, the dismissed view controller is never deallocated due to the presentedViewControllers property of my root view controller, which maintains a reference. In addition, each time a helper window is reopened, a new instance of the associated view controller is created rather than the previous instance, resulting in multiple instances and a memory leak.

How can a dismissed view controller be deallocated?

7 个答案:

答案 0 :(得分:0)

被解雇的视图控制器可以通过在解雇时不再保留任何被引用的视图控制器来解除分配。但是你说你 持有一个参考。因此,如果您在根视图控制器中持有引用,那么这不会发生。问题是:你为什么要提到它?你需要再次使用模型视图控制器吗?如果是,那么一切都很好 - 你需要它,所以它会留下来。

视图控制器未取消分配的另一个常见原因是其中的委托。在这种情况下,代表必须被标记为“弱”。

发布源代码会很有帮助。

答案 1 :(得分:0)

以下是我的视图控制器代码的基本要素:

class CoverIKImageViewController: NSViewController {

let zoomInFactor: CGFloat = 1.414214
let zoomOutFactor: CGFloat = 0.7071068

let center = NSNotificationCenter.defaultCenter()

var coverCGImage: CGImage!
weak var coverArtImageView: CoverImageView!
var imageProperties = [NSObject: AnyObject]()

@IBOutlet weak var toolControl: NSSegmentedControl!
@IBOutlet weak var resolution: NSTextField!

@IBOutlet weak var coverIKImageView: CoverIKImageView! {
    didSet {
        if coverIKImageView == nil { return }
        coverCGImage = coverArtImageView.image?.cgImage
        setIKImage(coverCGImage, url: coverArtImageView.imageURL)
        coverIKImageView.autoresizes = true
        coverIKImageView.delegate = self
    }
}

func setIKImage(cgImage: CGImage!, url: NSURL!) {
    if url == nil {
        imageProperties[kCGImagePropertyPixelWidth] = CGImageGetWidth(cgImage)
        imageProperties[kCGImagePropertyPixelHeight] = CGImageGetHeight(cgImage)
        coverIKImageView.setImage(cgImage, imageProperties: imageProperties)
    } else {
        coverIKImageView.setImageWithURL(url)
    }
    imageDidChange(coverIKImageView)
}

/* Additional functions here (not included) */

override func viewWillAppear() {
    self.view.window?.minSize = NSSize(width: 480, height: 560)
    setResolutionLabel()
    super.viewWillAppear()
}

override func viewDidAppear() {
    self.view.window?.makeFirstResponder(self.view)
    coverIKImageView.undoManager?.levelsOfUndo = 8

    let queue = NSOperationQueue.mainQueue()
    center.addObserverForName(Notification.NewImageDraggedToIKImageView, object: nil, queue: queue) { notification in
        self.imageDidChange(self.coverIKImageView)
    }
    super.viewDidAppear()
}

override func viewWillDisappear() {
    IKImageEditPanel.sharedImageEditPanel().close()
    coverIKImageView.undoManager?.removeAllActions()
    super.viewWillDisappear()
}

override func viewDidDisappear() {
    super.viewDidDisappear()
    coverCGImage = nil
    coverIKImageView.delegate = nil
    center.removeObserver(self)
}

}

以下是在代码中调用它的方式:

override func prepareForSegue(segue: NSStoryboardSegue, sender: AnyObject?) {
    if let identifier = segue.identifier {
        switch identifier {
        case "Edit Cover Image":
            if let evc = segue.destinationController as? CoverIKImageViewController {
                evc.coverArtImageView = coverArtView
            }
        default: break
        }
    }
}

答案 2 :(得分:0)

NSViewController属性" presentsViewControllers"不应该是内存泄漏的原因,因为只要呈现(呈现)视图控制器,它就只是保持引用。

这个怎么样?:

coverIKImageView.delegate = self

这个代表被宣布为弱吗?

你是如何检测内存泄漏的?请添加此项以检查所呈现的视图控制器是否确实未被释放:

deinit {
    NSLog("CoverIKImageViewController deinit called")
}

真的没有叫deinit吗?

答案 3 :(得分:0)

我已经问过你这行:

coverIKImageView.delegate = self

这个代表是什么?它是如何在CoverIKImageView中声明的? “弱”?

在这种情况下,仪器不会警告你“泄漏”。你必须做进一步的分析。短版本:在Xcode中按Cmd +“I”启动仪器 - >选择“分配” - >按红色按钮开始分析 - >使用您的应用程序并尝试产生内存泄漏 - >停止仪器 - >将“Allocation Lifespan”设置为“Created& Persistent” - >将分配类型设置为“分配列表” - >在SearchBar“仪器详细信息”中键入您的班级名称 - >你的班级现在应该在列表中 - >按下对象内存地址右侧的小箭头按钮 - >

enter image description here

您现在应该看到保留和释放类实例的对象列表。这会给你一个关于泄漏的提示。

答案 4 :(得分:0)

这正是我看到的问题:

Memory Leak with Bare Storyboard Project

我做了同样的事情,创建了一个像这个例子中的准系统项目。关闭隐藏窗口后,目标视图控制器永远不会被取消初始化,并为每个segue创建一个新的目标视图控制器实例。每个目标视图控制器实例的引用都保存在根视图控制器的presentsViewControllers属性中。

到目前为止,我看到的唯一解决方法是放弃使用segues,而是使用presentViewControllerAsSheet方法,将目标视图控制器的单个实例保存为我的根视图控制器中的属性。我还没有找到一种方法来启动具有指定目标视图控制器的segue。

答案 5 :(得分:0)

这行代码创建了一个参考周期:

<button onclick="alert($('#gr_start_dt').val());">Execute</button>

然而,这只是答案的一部分。即使没有这行代码,目标视图控制器也不会在被解除后解除分配。这行代码是罪魁祸首:

    center.addObserverForName(Notification.NewImageDraggedToIKImageView, object: nil, queue: queue) { notification in
    self.imageDidChange(self.coverIKImageView)
}

由于我不知道的原因,并且只是通过反复试验发现,用以下代码替换该行代码:

dismissViewController(self)

导致视图控制器在被解雇时被取消初始化。

答案 6 :(得分:0)

我还注意到,我的根ViewController的presentedViewControllers属性保留了其呈现的VC的多个副本。

经过一番挖掘,我发现我是在演示 VC上而不是演示 VC上调用dismiss(self)

    // From inside the presented VC:

    // Does dismiss, but is still retained by the presenting VC:
    self.dismiss(self)

    // Does dismiss, and also does not retain:
    presentingViewController?.dismiss(self)

奇怪的是,这两个选项都确实在视觉上关闭了我提出的VC,但是只有后者实际上将其从提出的VC的presentedViewControllers属性中删除。

谁能告诉我为什么前者实际上会在视觉上解散所呈现的VC?