You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为SceneDelegate的scene方法添加async后无法设置rootViewController?

问题描述

我在iOS项目中使用Coordinator模式,需要通过异步操作设置window.rootViewController。最初给SceneDelegate的scene(_:willConnectTo:options:)方法添加了async修饰符,代码如下:

class SceneDelegate: UIResponder, UIWindowSceneDelegate {
    var window: UIWindow?
    var coordinator : MainCoordinator!
    
    // 添加了async修饰符的scene方法
    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) async {
        guard let windowScene = (scene as? UIWindowScene) else { return }
        window = UIWindow(frame: UIScreen.main.bounds)
        window?.windowScene = windowScene
        coordinator = MainCoordinator(window: window!)

        await coordinator.start()
    }
}

MainCoordinator中设置根视图控制器的相关代码:

lazy var rootViewController: UIViewController = UIViewController()
lazy var tabCoordinator: TabCoordinator = TabCoordinator(tabBarController: rootViewController as! UITabBarController)

func start() async {
    if UserDefaults.standard.bool(forKey: "Logged") {
       await showTabVC()
    } else {
         showLoginVC()
    }
}

func showTabVC() async {
  rootViewController = tabBarController

  tabCoordinator = TabCoordinator(tabBarController: rootViewController as! UITabBarController)

  let tabVC = await tabCoordinator.start()
  window.rootViewController = tabVC
  window.makeKeyAndVisible()
}

TabCoordinator的start方法标记了@MainActor以确保UI更新正常,但运行后出现黑屏。

后来我移除了scene方法的async修饰符,把coordinator.start()放入Task{}中,功能恢复正常:

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    guard let windowScene = (scene as? UIWindowScene) else { return }

    window = UIWindow(frame: UIScreen.main.bounds)
    window?.windowScene = windowScene
    coordinator = MainCoordinator(window: window!)
    Task{
        await coordinator.start()
    }
}

想知道为什么给scene方法添加async会导致黑屏问题?

原因分析
  1. 系统对SceneDelegate回调的执行逻辑限制
    iOS系统调用scene(_:willConnectTo:options:)时,要求这个方法同步完成——它需要在方法执行结束后确认窗口和根视图控制器的初始化状态。给方法加上async修饰符后,系统会将其视为异步任务,方法会立即挂起并返回,此时窗口还没设置根视图控制器,系统就判定初始化完成,自然显示黑屏。

  2. 异步上下文的差异
    直接给系统回调方法加async,其运行上下文和手动创建Task的上下文不同。系统回调的异步方法不一定会在MainActor上执行(即便后续逻辑标记了@MainActor),而手动创建的Task默认继承当前上下文(SceneDelegate的方法本身在主线程执行,所以Task也会在主线程上下文运行),能保证UI更新时机正确。

  3. 窗口初始化的时机问题
    当scene方法为异步时,执行到await coordinator.start()会挂起,此时窗口已创建但未设置根视图控制器,系统可能在挂起阶段就完成了场景初始化流程,没有等待异步操作结束,导致窗口没有关联可见的视图控制器,最终出现黑屏。而用Task包裹时,scene方法会同步完成窗口创建和Coordinator初始化,之后在后台异步执行coordinator.start(),此时系统已确认窗口存在,后续设置根视图控制器的操作能正常生效。

内容的提问来源于stack exchange,提问作者Dzondzula

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 15:14:54