UITabBarController依赖注入失效:子ViewController中modelController为nil
问题根因与解决方案
核心根因
- 生命周期时序问题:UITabBarController的
viewDidLoad执行时机早于SceneDelegate中modelController的赋值时机。你在SceneDelegate给TabBar的modelController赋值前,TabBar的viewDidLoad已经跑完了,此时遍历子VC赋值用的modelController本身就是nil,最终子VC拿到的自然是空值。 - 子控制器嵌套问题:如果你的SongsViewController嵌套在UINavigationController中,TabBar的viewControllers数组里存的是UINavigationController实例,你直接判断是否为SongsViewController会匹配失败,不会执行赋值逻辑。
- 动态新增子控制器问题:如果TabBar的子控制器是在viewDidLoad之后动态添加的,遍历逻辑执行时这些子控制器还不存在,自然不会被赋值。
修复代码
把TabBarController的注入逻辑从viewDidLoad移到modelController的属性观察者中,同时兼容导航控制器嵌套的场景:
class TabBarController: UITabBarController { var modelController: ModelController! { didSet { // 只要modelController被赋值,就自动执行注入逻辑,不用关心时序问题 injectModelToChildViewControllers() } } override func viewDidLoad() { super.viewDidLoad() } private func injectModelToChildViewControllers() { guard let viewControllers = viewControllers else { return } for viewController in viewControllers { // 处理嵌套UINavigationController的场景 let targetVC: UIViewController? if let nav = viewController as? UINavigationController { targetVC = nav.topViewController } else { targetVC = viewController } // 匹配目标控制器赋值 if let songsVC = targetVC as? SongsViewController { songsVC.modelController = modelController } // 其他子控制器需要注入的话可以在这里扩展匹配规则 } } // 可选:如果有动态修改TabBar子控制器的需求,重写该方法保证新增的VC也能被注入 override func setViewControllers(_ viewControllers: [UIViewController]?, animated: Bool) { super.setViewControllers(viewControllers, animated: animated) injectModelToChildViewControllers() } }
快速调试验证方法
如果修复后仍然有问题,可以加两处打印快速定位:
- 在TabBar的
viewDidLoad中打印modelController的值,确认是否为nil - 在遍历viewControllers的循环中打印每个viewController的类型,确认是否匹配到SongsViewController
内容的提问来源于stack exchange,提问作者Zhou Haibo
相关产品推荐
相关产品推荐

