繁体   English   中英

关闭表视图后,单元格没有被释放,被闭包的上下文引用

[英]Cell isn't being deallocated after table view is dismissed, being referenced by closure's context

我正在创建一个自定义表格视图单元格,它允许用户添加、拍摄或查看上传的照片。

我发现即使在表格视图被解雇之后,这个单元格也会永远留在 memory 中,从而产生奇怪的memory graph 我希望正确关闭单元格,但我很难理解发生了什么。

该图显示我的单元格被addPhotoTapAction.context强烈引用。

addPhotoTapAction: ((ItemInfoCell) -> Void)? 是单元格的 class 变量,用于存储处理用户输入的闭包。 闭包在视图 controller 中定义:

let infocell = tableView.dequeueReusableCell(withIdentifier: K.infocellID) as! ItemInfoCell
    if item?.imageUrl == nil {
        self.imageManager.actionController?.actions[2].isEnabled = false
    } else {
        self.imageManager.actionController?.actions[2].isEnabled = true
    }
    infocell.addPhotoTapAction = { [unowned self] _ in
        infocell.addPhotoButton.isEnabled = false
        self.imageManager.pickImage(self) { [weak self] image in
            self?.imageToSave = image
            infocell.itemPhoto.image = self?.imageToSave
            infocell.addPhotoButton.tintColor = UIColor(ciColor: .clear)
            infocell.addPhotoButton.isEnabled = true
            self?.imageManager.actionController?.actions[2].isEnabled = true
        }

pickImage方法如下所示。 它用于展示带有图像选择器选项的动作 controller(拍照或从 lib 中选择):

func pickImage(_ viewController: UIViewController, _ callback: @escaping ((UIImage) -> ())) {
    picker.delegate = self
    picker.mediaTypes = ["public.image"]
    picker.allowsEditing = true
    pickImageCallback = callback
    self.viewController = viewController
    actionController!.popoverPresentationController?.sourceView = viewController.view
    viewController.present(actionController!, animated: true, completion: nil)
}

...并且回调被存储以在选择器的didFinishPickingMediaWithInfo调用中使用:

func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
    picker.dismiss(animated: true, completion: nil)
    if let image = info[.editedImage] as? UIImage {
        let squareImage = makeSquare(image)
        pickImageCallback?(squareImage)
    } else if let image = info[.originalImage] as? UIImage {
        let squareImage = makeSquare(image)
        pickImageCallback?(squareImage)
    }
    viewController = nil
}

我尝试手动将变量与闭包设置为nil ,然后从 [weak self] 切换到 [unowned self] 到两者的组合。 没运气。

我认为即使在使用 [weak/unowned] 捕获列表时, pickImage(self)或在闭包中使用类的属性都会创建强引用,但我仍然不确定并且无法修复它。

更新:ItemInfoCell 类的代码

class ItemInfoCell: UITableViewCell {

@IBOutlet weak var itemPhoto: UIImageView!
@IBOutlet weak var itemLabel: UILabel!
@IBOutlet weak var addPhotoButton: UIButton!

var addPhotoTapAction: ((ItemInfoCell) -> Void)?

override func awakeFromNib() {
    super.awakeFromNib()
}

override func setSelected(_ selected: Bool, animated: Bool) {
    super.setSelected(selected, animated: animated)
}

@IBAction func takePhoto(_ sender: Any) {
    if let addPhoto = self.addPhotoTapAction {
        addPhoto(self)
    }
}

}

问题是您在其回调中访问信息单元。 如果您在自己的回调中使用变量,则应通过将其添加到捕获列表来将其标记为弱。

在您的代码中,它应该是这样的:

   infocell.addPhotoTapAction = { [unowned self, weak infocell] _ in
      ...
    }

在您的代码中,从 infocell 到闭包和内部闭包有一个强引用,您指的是 infocell,即两者都对彼此有强引用。

infocell.addPhotoTapAction = { [unowned self] _ in
    infocell.addPhotoButton.isEnabled = false
     ...
}

这会导致保留周期,并且不允许释放单元格。

由于您已经在为自己使用捕获列表,您可以通过将 infocell 添加到捕获列表来轻松解决此问题,如下所示:

infocell.addPhotoTapAction = { [weak self, weak infocell] _ in
    guard let self = self else { return }
    infocell.addPhotoButton.isEnabled = false
    ...
}

一般来说,只有当你完全确定在执行闭包之前引用不会变为nil时,才建议使用unowned ,因为使用unowned就像强制解包一个可选值。 如果它是nil你的应用程序将崩溃。 所以weak self通常是 go 更安全的方法。

暂无
暂无

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

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