簡體   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