【问题标题】:Is weak self needed for table view cell button closure表格视图单元格按钮关闭是否需要弱自我
【发布时间】:2021-01-19 01:41:04
【问题描述】:

为了避免保留循环,是否有必要在 UITableViewCell 按钮操作中使用 [weak self] in?示例:

在 ViewController 的cellForRow

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

在 TableViewCell 类中

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

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

【问题讨论】:

  • 为什么不在内存图调试器中看一下,让你自己看看呢?这是一个很好的学习机会

标签: ios swift uitableview uibutton retain-cycle


【解决方案1】:

要考虑的关键是:

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

您提议在实例属性中长期存储一个函数

现在想想谁指的是/拥有谁。视图控制器拥有它的视图,该视图是或拥有拥有该单元格的表视图。同时细胞拥有该功能。如果函数强烈引用我刚才提到的任何对象,那就是一个保留循环。这是一个经典保留循环,是如何制作保留循环的绝对模型。


[我想补充一下我是如何测试这些东西的。有一个非常便宜且简单的方法:将您的视图控制器包装在一个导航控制器和一个空白的根视图控制器中,这样您就可以push将您的视图控制器放到它上面。在您的视图控制器中实现deinit。现在运行应用程序,按下视图控制器,玩一会儿,然后使用返回按钮弹出它。如果deinit 没有被调用,那么你就有了一个保留周期。]

【讨论】:

  • 能否请您用很少的代码编写如何在点击后退按钮时测试从导航 vc 中提取的 vc ?你喜欢这种测试的扩展还是像你说的那样写几行代码。我应该为我的问题提出一个新问题还是在这里可以?
【解决方案2】:

是的,这种情况下需要使用unownedweak来捕获self

  • 您的视图控制器很可能会强烈引用UITableView
  • 表格视图强烈引用它的UITableViewCells
  • 每个单元格都强烈引用您的 buttonAction 闭包。

直接使用 self 会产生一个保留循环的效果。

这实际上很容易测试。尝试呈现以下视图控制器并将其关闭:

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)
    }
}

您将获得以下输出:

TestTableViewController deinit!
TestTableViewCell deinit!

现在呈现相同的视图控制器,没有弱,你会看到没有输出,这意味着 deinit 函数没有被调用并且对象留在内存中。

【讨论】:

    【解决方案3】:

    如果您不使用 tableviewcell 的对象,并且只想对单元格执行操作,请执行此操作。

    var buttonAction: (() -> Void)?
    
    @IBAction func buttonPressed(_ sender: Any) {
         buttonAction?()
    }
    

    在 cellForRow 上使用这个,

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

    对于检查保留周期,我通常使用此选项检查内存是否保留。

    【讨论】:

      猜你喜欢
      • 2018-03-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多