为什么初始化PlannerViewController后其tableView outlet值为nil
问题原因
本质是UIViewController的视图懒加载机制导致的差异:
UITableViewController系统默认实现的loadView方法会在首次访问tableView属性时自动创建tableView实例,所以你实例化todayVC、reviewVC之后就算没有展示,访问它们的tableView也不会为nil。- 你自定义的
PlannerVC是普通UIViewController子类,tableView是通过@IBOutlet关联的Storyboard出口,只有当控制器的视图完成加载(也就是viewDidLoad执行完毕)时,IBOutlet才会被系统赋值连接。你在当前PageViewController的viewDidLoad里仅实例化了plannerVC,没有触发它的视图加载流程(初始显示的是todayVC,plannerVC还没有被加载展示),所以此时plannerVC.tableView必然是nil,回调触发调用reloadData就会崩溃。当你滑动到plannerVC页面时,系统会触发它的视图加载流程,IBOutlet完成连接,之后再调用就不会崩溃。
解决方案
你可以根据自己的业务场景选以下任意一种方案:
- 用可选链加判空,避免空调用崩溃:
把回调里的刷新代码改成self.plannerVC.tableView?.reloadData(),可选链会在tableView为nil时自动忽略后续调用,不会崩溃,等plannerVC视图加载完成后,下一次回调就能正常刷新。 - 主动触发plannerVC的视图加载:
在实例化plannerVC之后加一行代码_ = plannerVC.view,手动访问view属性会强制触发控制器的视图加载流程,完成IBOutlet的连接,此时tableView就会被赋值,后续调用就不会为空。如果你的控制器数量不多,这个方案实现最简单。 - 在PlannerVC内部处理刷新逻辑:
把数据源回调的监听放到PlannerVC内部,在viewDidLoad之后再执行首次刷新,回调触发时如果视图还没加载,就先把最新数据暂存,等视图加载完成后再执行刷新。 - 将PlannerVC改为UITableViewController子类:
和另外两个控制器保持一致,系统会自动管理tableView的创建,实例化后访问tableView就不会为空。
// 可选链修改示例 let taskListDataSource = TaskListDataSource { self.todayVC.tableView.reloadData() self.plannerVC.tableView?.reloadData() self.reviewVC.tableView.reloadData() }
内容的提问来源于stack exchange,提问作者Jack
相关产品推荐
相关产品推荐

