简体   繁体   English

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

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

I'm creating a custom table view cell, which allows the user to add, take, or view uploaded photos.我正在创建一个自定义表格视图单元格,它允许用户添加、拍摄或查看上传的照片。

I discovered that this cell stays in memory forever even after the table view's dismissal, creating weird memory graph .我发现即使在表格视图被解雇之后,这个单元格也会永远留在 memory 中,从而产生奇怪的memory graph I want the cell to be dismissed properly , but I have a hard time understanding what is going on.我希望正确关闭单元格,但我很难理解发生了什么。

The graph shows that my cell is being strongly referenced by a addPhotoTapAction.context .该图显示我的单元格被addPhotoTapAction.context强烈引用。

addPhotoTapAction: ((ItemInfoCell) -> Void)? addPhotoTapAction: ((ItemInfoCell) -> Void)? is the cell's class variable, used to store the closure handling user input.是单元格的 class 变量,用于存储处理用户输入的闭包。 The closure is defined in the view controller:闭包在视图 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
        }

The pickImage method is shown below. pickImage方法如下所示。 It's used to present action controller with image picker options (take photo or choose from lib):它用于展示带有图像选择器选项的动作 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)
}

...and the callback is stored to be used in picker's didFinishPickingMediaWithInfo call: ...并且回调被存储以在选择器的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
}

I've tried to manually set variable with closure to nil , then switched from [weak self] to [unowned self] to the combination of both.我尝试手动将变量与闭包设置为nil ,然后从 [weak self] 切换到 [unowned self] 到两者的组合。 No luck.没运气。

I think that either the pickImage(self) , or using the class' properties within the closure are creating a strong reference even when using [weak/unowned] capture lists, but I'm still not sure and not being able to fix it.我认为即使在使用 [weak/unowned] 捕获列表时, pickImage(self)或在闭包中使用类的属性都会创建强引用,但我仍然不确定并且无法修复它。

Update: ItemInfoCell class' code更新: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)
    }
}

} }

The problem is that you access the infocell inside it's callback.问题是您在其回调中访问信息单元。 If you use variable inside own callback, you should mark it as a weak by adding it to the capture list.如果您在自己的回调中使用变量,则应通过将其添加到捕获列表来将其标记为弱。

In your code it should be like this:在您的代码中,它应该是这样的:

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

In your code, there is a strong reference from infocell to closure and inside closure you are referring to infocell ie both have a strong reference to each other.在您的代码中,从 infocell 到闭包和内部闭包有一个强引用,您指的是 infocell,即两者都对彼此有强引用。

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

This leads to a retain cycle and doesn't allow the cell to be deallocated.这会导致保留周期,并且不允许释放单元格。

Since you are already using capture list for self, you can easily solve this issue by adding infocell to the capture list, like so:由于您已经在为自己使用捕获列表,您可以通过将 infocell 添加到捕获列表来轻松解决此问题,如下所示:

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

Generally speaking, it is suggested to use unowned only when you are completely sure that the reference will not become nil before execution of the closure, because using unowned is like forcefully unwrapping an optional value.一般来说,只有当你完全确定在执行闭包之前引用不会变为nil时,才建议使用unowned ,因为使用unowned就像强制解包一个可选值。 If it is nil your app will crash.如果它是nil你的应用程序将崩溃。 So weak self is generally the safer way to go.所以weak self通常是 go 更安全的方法。

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

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