表视图被解散后,单元格没有被deallocated,而是被闭包的上下文引用。

问题描述 投票:0回答:1

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

我发现,这个单元格即使在表视图解散后也会永远留在内存中,从而产生了奇怪的 记忆图谱. 我想让牢房正常解散但我很难理解这是怎么回事。

图中显示,我的单元格被一个强烈的 addPhotoTapAction.context.

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

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
        }

视图控制器中定义了 挑选图片 方法如下所示。它用于向动作控制器展示图像拾取器选项(拍照或从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)
}

...并且回调被存储在picker的回调中使用 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
}

我试着手动设置变量,并将其封闭为 ,然后从【弱小的自己】切换到【无主的自己】,再到两者的组合。运气不好。

我想,无论是 pickImage(self)或者在闭合中使用类的属性,即使在使用[weakunowned]捕获列表时也会产生强引用,但我仍然不确定,也无法解决这个问题。

更新: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)
    }
}

}

ios swift closures retain-cycle
1个回答
0
投票

问题是你在回调中访问了infocell。如果你在自己的回调中使用了变量,你应该把它添加到捕获列表中,把它标记为弱。

在你的代码中,它应该是这样的。

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

0
投票

在你的代码中,有一个从infocell到closure的强引用,而在closure里面,你是在引用infocell,也就是说,两者都有一个强引用给对方。

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

这导致了一个保留循环,并且不允许单元格被deallocated。

由于你已经为自己使用了捕获列表,你可以很容易地解决这个问题,将infocell添加到捕获列表中,像这样。

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

一般来说,建议使用 unowned 只有当您完全确定该参考资料不会成为......。nil 在执行闭包之前,因为使用 unowned 就像强行拆开一个可选的值。如果它是 nil 你的应用程序将崩溃。所以 weak self 一般来说是比较安全的方法。

© www.soinside.com 2019 - 2024. All rights reserved.