iOS12.4中ViewWillDisappear调用popToRootViewController崩溃求助
你遇到的这个场景其实是iOS13之前UIKit视图生命周期与导航栈操作冲突的典型问题,我来帮你拆解分析并给出可行的解决思路:
问题场景复盘
- 你的TabBar包含5个NavigationController,第一个Nav的
viewWillDisappear中调用popToRootViewController会崩溃,提示UITableView dataSource未设置,但第三个Nav执行相同操作完全正常 - 不管
animated设为true还是false都会崩溃,升级到iOS13后问题自动消失 - 在
tabBar(_:didSelect:)方法中调用第一个Nav的popToRootViewController却能正常工作
核心原因分析
iOS13之前,UIKit对TabBar切换时的视图生命周期管理不够严谨:当你切换TabBarItem时,第一个Nav的viewWillDisappear会在TabBar切换动画的过程中触发。此时调用popToRootViewController,会导致栈中的MyViewController(带UITableView的那个)被提前释放,但它的UITableView可能还在执行布局或数据源相关操作——因为视图控制器已经被释放,TableView自然找不到对应的数据源,从而抛出异常。
而第三个Nav没问题,大概率是其导航栈结构、视图控制器的释放时机和第一个Nav存在差异;tabBar(_:didSelect:)中调用正常,则是因为这个方法是在TabBar切换完全完成后触发的,此时视图生命周期状态稳定,不会出现视图控制器提前释放的情况。
可行的解决办法
按推荐优先级给你整理了几个方案:
1. 在MyViewController的deinit中置空TableView的数据源/代理(根源解决)
直接从问题的触发点入手,确保视图控制器被释放前,主动切断TableView与数据源/代理的关联:
deinit { tableView.dataSource = nil tableView.delegate = nil }
这个方法能彻底避免“数据源未设置”的崩溃,哪怕导航栈操作时机存在问题,也不会触发异常。
2. 延迟执行pop操作(快速临时修复)
把popToRootViewController的调用延迟到主队列的后续循环,等视图切换的生命周期操作完成后再执行:
override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) // 延迟0.1秒,让视图切换的动画和生命周期流程先完成 DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { self.popToRootViewController(animated: false) } }
3. 改用viewDidDisappear执行pop
viewDidDisappear是在视图完全消失后才触发的,此时视图控制器状态稳定,不会出现子视图还在操作的情况:
override func viewDidDisappear(_ animated: Bool) { super.viewDidDisappear(animated) self.popToRootViewController(animated: false) }
缺点是用户可能会看到视图消失后再执行pop的延迟感,视觉体验略有影响。
4. 检查自定义TabBar的setBar方法是否干扰生命周期
你的自定义TabBar有setBar方法用于隐藏/显示TabBar,要确保动画过程中没有修改视图层级或约束,导致第一个Nav的视图控制器被提前释放。可以尝试在执行pop操作前,先完成TabBar的动画:
// 比如在viewWillDisappear中先完成TabBar动画再pop override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) tabBarController?.setBar(hidden: true, animated: false) { _ in self.popToRootViewController(animated: false) } }
额外说明
你提到iOS13后问题自动解决,是因为苹果在iOS13中重构了UIKit的视图生命周期管理,尤其是优化了导航控制器和TabBar控制器的视图栈管理逻辑,从底层避免了这种视图控制器提前释放的情况。
内容的提问来源于stack exchange,提问作者frank61003

