[英]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
,如果没有更强的引用,那么闭包就会被释放并且强引用循环被破坏)。
但是,有一些观察结果:
我无法仅使用提供的代码重现您的强引用周期。
但是如果呈现editImageViewController
的视图控制器保持editImageViewController
的强引用,则可以引发强引用循环,从而阻止它被释放。 editImageViewController
是局部变量,还是属性? 如果是属性,请将其更改为本地变量,这可能会解决问题。 或许其他东西保持对editImageViewController
的强引用(例如重复计时器或类似的东西)。
您可以使用“调试内存图”工具来识别对editImageViewController
的强引用(参见下面的第3点)。
如果你确实想确保attachToComment
的EditImageViewController
没有保持对呈现视图控制器的强引用,我会替换:
editImageViewController.attachToComment = attachToComment
有:
editImageViewController.attachToComment = { [weak self] data, url, type in self?.attachToComment(data, url: url, type: type) }
这确保了最简单的方法attachToComment
关闭在editImageViewController
不保持强大的参考呈现视图控制器。
您可能希望使用Xcode 8中的“调试内存图”工具来识别EditImageViewController
的所有权。 例如,我做了一个例子,我都是:
将editImageViewController
错误地存储为呈现视图控制器的属性,而不是将其保存为局部变量(第1点); 和
没有在封闭中使用weak
模式(第2点)。
当我这样做时,我得到了一个像这样的记忆图:
从该图表中,我不必猜测问题的根源是什么。 我不仅可以看到强引用周期存在,而且具体是导致问题的原因。 但是,如果我修复上述两个问题中的任何一个(或两者),这个循环就会消失。
问题是当您分配attachToComment时,它会捕获视图控制器的自指针,并阻止发布。
有两种方法可以解决这个问题:
编辑由于Rob的更正,而不是捕获实例,请使用弱自指针获取闭包:
在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.