[英]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.