简体   繁体   English

表格视图单元格按钮关闭需要弱自身

[英]Is weak self needed for table view cell button closure

In trying to avoid retain cycles, would using [weak self] in in a UITableViewCell button action be necessary?为了避免保留循环,是否需要在 UITableViewCell 按钮操作中使用[weak self] in Example:例子:

in ViewController's cellForRow在 ViewController 的cellForRow

cell.buttonAction = { (cell) [weak self] in
     self.someFunction()
}

in TableViewCell class在 TableViewCell 类中

var buttonAction: ((UITableViewCell) -> Void)?

@IBAction func buttonPressed(_ sender: Any) {
     buttonAction?(self)
}

The key line to think about is:要考虑的关键线是:

var buttonAction: ((UITableViewCell) -> Void)?

You are offering to store a function long-term in an instance property.您提供将函数长期存储在实例属性中。

Now think about who refers to / owns whom.现在想想谁指/拥有谁。 The view controller owns its view which is-or-owns the table view which owns the cell.视图控制器拥有它的视图,该视图是拥有单元格的表视图。 Meanwhile the cell owns the function.同时细胞拥有该功能。 If the function refers strongly to any of the objects I just mentioned, that is a retain cycle .如果函数强烈引用我刚才提到的任何对象,那就是一个保留循环 It is a classic retain cycle, the absolute model of how retain cycles get made.这是一个经典的保留循环,是保留循环如何制作的绝对模型。


[I would like to add a word about how I test for these things. [我想补充一下我如何测试这些东西。 There's a really cheap and easy way: wrap your view controller up in a navigation controller plus a blank root view controller, so that you can push your view controller onto it.有一个非常便宜和简单的方法:将您的视图控制器包装在一个导航控制器和一个空白的根视图控制器中,这样您就可以您的视图控制器推送到它上面。 Implement deinit in your view controller.在您的视图控制器中实现deinit Now run the app, push your view controller, play with it for a bit, and pop it with the Back button.现在运行应用程序,推动你的视图控制器,玩一会儿,然后用后退按钮弹出它。 If deinit isn't called, you've got a retain cycle.]如果未调用deinit ,则您有一个保留周期。]

Yes, it is necessary to use unowned or weak to capture self in this case.是的,在这种情况下有必要使用unownedweak来捕获self

  • Your view controller will most likely have a strong reference to the UITableView您的视图控制器很可能对UITableView有很强的引用
  • The table view has strong reference to it's UITableViewCells and表格视图强烈引用它的UITableViewCells
  • Each cell has strong reference to your buttonAction closure.每个单元格都有对buttonAction闭包的强引用。

Using self directly will have as an effect a retain cycle.直接使用 self 会产生一个保留循环的效果。

This is actually pretty easy to test.这实际上很容易测试。 Try to present the following view controller and dismiss it:尝试呈现以下视图控制器并将其关闭:

class TestTableViewCell: UITableViewCell {
    var closure: (() -> Void)?
    
    deinit {
        print("TestTableViewCell deinit!")
    }
}

class TestTableViewController: UITableViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        tableView.register(TestTableViewCell.self, forCellReuseIdentifier: "TestTableViewCellIdentifier")
    }
    
    deinit {
        print("TestTableViewController deinit!")
    }
    
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 1
    }
    
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "TestTableViewCellIdentifier", for: indexPath) as! TestTableViewCell
        
        cell.closure = { [weak self] in
            guard let self = self else { return }
            self.testFunction()
        }
        
        return cell
    }
    
    func testFunction() {}
}

// Test present
let controller = TestTableViewController()
present(controller, animated: true) {
    DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
        self.dismiss(animated: true)
    }
}

You will have the following output:您将获得以下输出:

 TestTableViewController deinit! TestTableViewCell deinit!

Now presenting the same view controller without weak and you will see that there is no output, meaning that the deinit functions are not get called and the objects stay in the memory.现在展示没有weak的相同视图控制器,您将看到没有输出,这意味着没有调用deinit函数并且对象保留在内存中。

if you're not using the object of tableviewcell and you only want execute action on the cell make this.如果您不使用 tableviewcell 的对象并且只想在单元格上执行操作,请执行此操作。

var buttonAction: (() -> Void)?

@IBAction func buttonPressed(_ sender: Any) {
     buttonAction?()
}

and on the cellForRow use this,在 cellForRow 上使用这个,

cell.buttonAction = { [weak self] in
     self?.someFunction()
}

for check retain cycles, I usually use this option for check if the memory is retain or not.对于检查保留周期,我通常使用此选项来检查内存是否保留。 在此处输入图片说明

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

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