【问题标题】:avoiding retain cycles for UITableView done programmatically from a ViewController that is a child of another ViewController避免从另一个 ViewController 的子 ViewController 以编程方式完成 UITableView 的保留周期
【发布时间】:2018-06-04 16:17:37
【问题描述】:

我有一个名为 MainViewController 的 ViewController,它充当应用程序的中心页面。这个 MainViewController 有 6 个属性,它们是其他 viewControllers 的实例

lazy var shelvesView: ShelvesViewController = {
    return ShelvesViewController()
}()
lazy var goalsView: GoalsViewController = {
    return GoalsViewController()
}()
lazy var shoppingView: ShoppingListViewController = {
    return ShoppingListViewController()
}()
lazy var tipsView: TipsViewController = {
    return TipsViewController()
}()
lazy var myDenView: MyDenViewController = {
    return MyDenViewController()
}()
lazy var settingsview: SettingsViewController = {
    return SettingsViewController()
}()

当 mainViewController 加载时,它会从在 mainViewControllers 自定义 NavBar 下方放置的架子视图控制器开始,就像这样

func setupShelvesView() {
    shelvesView.willMove(toParentViewController: self)
    addChildViewController(shelvesView)
    self.view.addSubview(shelvesView.view)
    shelvesView.view.frame = CGRect(x: 0, y: view.frame.height * 0.08, width: view.frame.width, height: view.frame.height - (view.frame.height * 0.08))
    shelvesView.didMove(toParentViewController: self)
    globalCurrentView = 1
}

我还有一个菜单可以在这个 mainViewController 上滑动,其中包含用户可以导航到的不同页面的列表。当用户切换到新页面时,比如goalsViewController,shelfViewController 将在屏幕外显示动画,从 parentViewController(MainViewController) 中移除,goalsViewController 将被初始化,移动到 MainViewController 并在与shelfView 相同的帧中显示动画。每当我从菜单中切换 VC 时,我都会使用此方法。

func changeVCfrom(OldVC oldVC: UIViewController, newVC: UIViewController) {

    let newStartFrame = CGRect(x: 0 + self.view.frame.width, y: 0, width: view.frame.width, height: view.frame.height - (view.frame.height * 0.08))

    let newEndframe = CGRect(x: 0, y: view.frame.height * 0.08, width: view.frame.width, height: view.frame.height - (view.frame.height * 0.08))

    let oldfinishFrame = CGRect(x: 0 - self.view.frame.width, y: 0, width: self.view.frame.width, height: self.view.frame.height)

    oldVC.willMove(toParentViewController: nil)
    self.addChildViewController(newVC)
    newVC.view.frame = newStartFrame


    transition(from: oldVC, to: newVC, duration: 0.2, options: [.curveEaseOut], animations: {
        oldVC.view.frame = oldfinishFrame
        newVC.view.frame = newEndframe
    }, completion: { (success) in
        oldVC.willMove(toParentViewController: nil)
        oldVC.view.removeFromSuperview()
        oldVC.removeFromParentViewController()
        newVC.willMove(toParentViewController: self)
        self.view.addSubview(newVC.view)
    })
}

这里的问题是我已经以编程方式完成了所有视图,并且上面任何具有 tableView 属性的 viewControllers(惰性属性)都有内存泄漏。每当 VC 离开 MainViewController 时,用于 tableView 的内存都不会被释放,而每当它返回 MainViewController 时,就会再次分配更多内存。

到目前为止,这就是我设置 tableViews 的方式

let tableView = UITableView()

override func viewDidLoad() {
    super.viewDidLoad()
    setupObjects()
    tableView.register(ShelfTableViewCell.self, forCellReuseIdentifier: "shelfCell")
}
func setupTableView() {
    view.addSubview(tableView)
    tableView.delegate = self
    tableView.dataSource = self
    setupTableViewConstraints()
}
func setupTableViewConstraints() {
    tableView.translatesAutoresizingMaskIntoConstraints = false
    tableView.topAnchor.constraint(equalTo: view.topAnchor, constant: 0).isActive = true
    tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0).isActive = true
    tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 0).isActive = true
    tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0).isActive = true
}

我知道我需要让我的 tableviews 弱、无主或以某种方式让 tableview 在屏幕外完全释放。但无论我尝试什么,我都会得到我无法解决的错误,或者我的 ViewController 无法加载 tableview。对于这个冗长的问题,我深表歉意,但非常感谢您对此提供任何帮助。

【问题讨论】:

    标签: swift memory-management memory-leaks


    【解决方案1】:

    当您的子视图控制器离开屏幕时,表视图不会被释放。

    您的 MainViewController 拥有那些其他视图控制器。是的,它们很懒惰,但是一旦它们第一次被调用,它们就会与它们拥有的 tableviews 一起被初始化。

    子视图控制器不会通过简单地将它们移出屏幕来释放。您所做的只是更改框架并将它们作为 MainViewController 的子视图控制器删除。由于它们仍在内存中,因此表格视图也在内存中。

    如果你真的想在每次控制器移出屏幕时释放表格视图,你可以设置

    self.tableView = nil
    

    这里不应该有retain循环,因为tableView的datasource和delegate属性是weak,这意味着它们不会增加视图控制器的引用计数。

    【讨论】:

    • 我发现内存泄漏不是来自tableview,而是来自dequeReusableCell。感谢您的帮助!
    【解决方案2】:

    在 tableView 上上下滚动时,我的内存会急剧增加。原来我设置了 dequeReusableCell 错误。

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = ShelfTableViewCell(style: UITableViewCellStyle.value1, reuseIdentifier: "shelfCell")
        tableView.dequeueReusableCell(withIdentifier: "shelfCell", for: indexPath)
        if let shelf = UserController.shared.user?.shelves?[indexPath.row] as? Shelf {
            cell.shelf = shelf
            return cell
        } else {
            return UITableViewCell()
        }
    }
    

    基本上发生的事情是每次点击 viewWillAppear 时,我都会重新加载运行此代码的 tableViewData

        let cell = ShelfTableViewCell(style: UITableViewCellStyle.value1, reuseIdentifier: "shelfCell")
        tableView.dequeueReusableCell(withIdentifier: "shelfCell", for: indexPath)
    

    导致内存泄漏的原因

    用下面的代码替换上面的代码解决了这个问题

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "shelfCell", for: indexPath) as! ShelfTableViewCell
        if let shelf = UserController.shared.user?.shelves?[indexPath.row] as? Shelf {
            cell.shelf = shelf
            return cell
        } else {
            return UITableViewCell()
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-01-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多