从Storyboard实例化导航控制器时根VC重复引发模态展示崩溃
这个问题我做项目时也碰到过,核心原因其实是你的根视图控制器实例被重复绑定到了不同的导航控制器层级里——哪怕你每次实例化的是新的导航控制器,但只要根VC是同一个已经处于活跃状态的实例,系统就会判定它“正在被使用”,直接抛出这个NSInvalidArgumentException异常。
问题根源拆解
你调试时发现nav != nav_1但rootVC == rootVC_1,这就说明你两次实例化导航控制器时,复用了同一个根VC实例。这个根VC已经属于前一个导航控制器的视图层级(哪怕前一个导航控制器可能已经被你 dismiss 了,但如果根VC被强引用没释放,它依然会被标记为active),当你把它塞进新的导航控制器再去模态展示时,系统就会报错。
具体解决办法
1. 确保每次实例化导航控制器时,根VC都是全新实例
这是最稳妥、推荐的方案。你要避免手动复用根VC实例,直接让Storyboard帮你创建全新的导航控制器和根VC:
// Swift 代码示例 let mainStoryboard = UIStoryboard(name: "Main", bundle: nil) // 用导航控制器的Storyboard ID直接实例化,系统会自动创建新的根VC guard let targetNav = mainStoryboard.instantiateViewController(withIdentifier: "YourNavControllerID") as? UINavigationController else { print("导航控制器实例化失败") return } present(targetNav, animated: true)
// Objective-C 代码示例 UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil]; UINavigationController *targetNav = [mainStoryboard instantiateViewControllerWithIdentifier:@"YourNavControllerID"]; [self presentViewController:targetNav animated:YES completion:nil];
⚠️ 注意:不要在代码里做类似targetNav.viewControllers = @[self.reusedRootVC]这种手动替换根VC的操作,除非你能100%确认这个reusedRootVC已经完全脱离了之前的视图层级。
2. 排查根VC的强引用问题
如果按照上面的方法还是出现问题,那大概率是你的根VC被某个全局对象、单例或者其他强引用持有了,导致它没法被正常释放。你可以在根VC的生命周期方法里加日志验证:
// Swift deinit { print("根VC被释放了:\(self)") }
// Objective-C - (void)dealloc { NSLog(@"根VC被释放了:%@", self); }
如果模态关闭导航控制器后,根VC没有触发deinit/dealloc,那就要去排查哪里有不必要的强引用,把它移除掉,保证根VC能正常销毁,下次实例化时就是全新的对象。
3. 若必须复用根VC,先彻底移除原层级关联
如果业务需求确实需要复用根VC实例,那在把它加入新导航控制器之前,一定要先让它脱离原来的视图层级:
// Swift 示例 // 先从原父控制器中移除 reusedRootVC.removeFromParent() // 移除视图 reusedRootVC.view.removeFromSuperview() // 重置导航栈相关的关联 reusedRootVC.navigationController = nil // 再创建新的导航控制器并设置根VC let newNav = UINavigationController(rootViewController: reusedRootVC) present(newNav, animated: true)
不过这种方式不推荐,很容易引发后续的视图层级管理问题,除非万不得已,还是优先用第一种方案。
内容的提问来源于stack exchange,提问作者TheFuquan

