繁体   English   中英

传递闭包导致内存泄漏

[英]Passing a closure causing a memory leak

我有以下功能:

func attachToComment(_ data: Data, url: URL?, type: MediaType) {
    self.dismiss(animated: true, completion: nil)
    let image = UIImage(data: data)!
    model.attachmentData = data
    model.attachmentVideoURL = url
    model.attachmentType = type

    toolbar.attachImage(image, withType: type)
}

这是传递给模态呈现的视图控制器的引用:

containerNavViewController.attachToComment = attachToComment

ContainerNavViewController是一个UINavigationController ,里面有一个容器视图控制器。 这显示了摄像头,一旦拍摄了图像, attachToComment函数传递给下一个视图控制器 - EditImageViewController 在ContainerNavViewController里面我有以下代码:

var attachToComment: ((_ data: Data, _ url: URL?, _ type: MediaType) -> ())?
var model = CameraModel()
....

editImageViewController.attachToComment = self.attachToComment
editImageViewController.model = model
self.navigationController?.pushViewController(editImageViewController, animated: false)

在EditImageViewController里面我有以下代码调用闭包:

attachToComment(model.imageData, model.videoURL, model.mediaType)

然后调用原始函数并解除模态视图控制器。 然而,deinit并未在这些视图中被调用,并且它们在后台非常“活跃”。 我的内存泄漏在哪里?我该如何预防?

正如Dare和Sealos所暗示的那样,问题很可能是一个强大的参考周期。 但是问题中提供的代码,单独来说,还不足以导致这样的循环(因为当你解除editImageViewController ,如果没有更强的引用,那么闭包就会被释放并且强引用循环被破坏)。

但是,有一些观察结果:

  1. 我无法仅使用提供的代码重现您的强引用周期。

    但是如果呈现editImageViewController的视图控制器保持editImageViewController的强引用,则可以引发强引用循环,从而阻止它被释放。 editImageViewController是局部变量,还是属性? 如果是属性,请将其更改为本地变量,这可能会解决问题。 或许其他东西保持对editImageViewController的强引用(例如重复计时器或类似的东西)。

    您可以使用“调试内存图”工具来识别对editImageViewController的强引用(参见下面的第3点)。

  2. 如果你确实想确保attachToCommentEditImageViewController没有保持对呈现视图控制器的强引用,我会替换:

     editImageViewController.attachToComment = attachToComment 

    有:

     editImageViewController.attachToComment = { [weak self] data, url, type in self?.attachToComment(data, url: url, type: type) } 

    这确保了最简单的方法attachToComment关闭在editImageViewController不保持强大的参考呈现视图控制器。

  3. 您可能希望使用Xcode 8中的“调试内存图”工具来识别EditImageViewController的所有权。 例如,我做了一个例子,我都是:

    • editImageViewController错误地存储为呈现视图控制器的属性,而不是将其保存为局部变量(第1点);

    • 没有在封闭中使用weak模式(第2点)。

    当我这样做时,我得到了一个像这样的记忆图:

    对象图

    从该图表中,我不必猜测问题的根源是什么。 我不仅可以看到强引用周期存在,而且具体是导致问题的原因。 但是,如果我修复上述两个问题中的任何一个(或两者),这个循环就会消失。

问题是当您分配attachToComment时,它会捕获视图控制器的自指针,并阻止发布。

有两种方法可以解决这个问题:

  1. 当视图被解除时,没有attachToComment变量。

编辑由于Rob的更正,而不是捕获实例,请使用弱自指针获取闭包:

  1. 在attachToComment变量中使用弱自指针。 像这样:

     typealias ReturnType = (Data, URL?, MediaType) -> () func attachToComment() -> ReturnType { return { [weak self] in self?.dismiss(animated: true, completion: nil) let image = UIImage(data: data)! self?.model.attachmentData = data self?.model.attachmentVideoURL = url self?.model.attachmentType = type self?.toolbar.attachImage(image, withType: type) } } editImageViewController.attachToComment = containerNavController.attachToComment() 

问题是,这通常使用弱协议来完成。 一个例子是:

protocol EditImageProtocol: class {
    func attachToComment(_ data: Data, url: URL?, type: MediaType)
}

class ContainerNavController: EditImageProtocol {
    func attachToComment(_ data: Data, url: URL?, type: MediaType) {
        self.dismiss(animated: true, completion: nil)
        let image = UIImage(data: data)!
        model.attachmentData = data
        model.attachmentVideoURL = url
        model.attachmentType = type

        toolbar.attachImage(image, withType: type)
    }
}

class EditImageViewController {
    weak var delegate: EditImageProtocol?

    func someFunction() {
        delegate?.attachToComment(...)
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM