iOS Prewarm机制可能增加Bugly崩溃

570 阅读3分钟

前言

UIScene 是什么|在 iOS 13 之前,在功能职责上,UIApplication 负责 App 状态,UIApplicationDelegate(AppDelegate)负责 App 事件和生命周期,包括进程和 UI 的。对于单窗口的 App 来说这没有问题,但是要想开发多窗口的 iPad App 或者 Mac Catalyst App 的话,这种功能职责的划分已经不支持了。因此,Apple 于 iOS 13 引入用于构建多窗口应用的 UIScene,并对功能职责进行了拆分,将 UI 相关的状态、事件和生命周期交与 UIWindowSceneUIWindowSceneDelegate(SceneDelegate)负责,UISceneSession 负责持久化的 UI 状态。

问题

extension SceneDelegate {
    static var shared: SceneDelegate! {
        UIApplication.shared.connectedScenes.first?.delegate as! SceneDelegate
    }
}

猜想上面代码在启动的时候调用会崩溃吗?答案肯定是会的,结合上面前言部分说明,App在启动时候是优先执行UIApplication相关代理方法,然后才会去生成程序指定的SceneDelegate,如果在SceneDelegate生成之前在application(_ application:, didFinishLaunchingWithOptions launchOptions:)中调用SceneDelegate实例就会崩溃。一开始自己对这块儿没有太多关注,像版本更新、启动广告等业务逻辑的调用时机放在了didFinishLaunchingWithOptions中,然后由于业务中会展示弹窗,弹窗走的统一的window,其中引用到了SceneDelegate的shared,导致bugly上崩溃挺多,但是测试同学和自己开发过程中确实也没有出现过,包括各种弱网尝试均未复现。

知其崩溃原因,但是又不知其所以然。

截屏2024-06-21 10.50.32.png 经过对bugly崩溃记录的分析,发现大多数崩溃为凌晨,且崩溃机型大多为好手机,且iOS15以上,且无用户反馈问题,再结合SceneDelegate与Appdelegate的区别,知晓苹果针对iOS15以上启用的prewarm机制,猜想必是这个原因所致。

prewarm 机制|Apple 在 iOS 15 中引入了 prewarm (预热)机制,系统可能会根据设备的情况,prewarm 你的 App —— 启动不在运行的 App 进程,以减少用户手动启动 App 等待的时间。prewarm 执行一个 App 的启动序列直到(但不包括)当 main() 调用 UIApplicationMain。这为系统提供了一个机会来构建和缓存它需要的任何低层结构,以期待一个完整的启动。也就是说,prewarm 机制可以减少启动时间,我们甚至可以在 load 方法中做一些资源的预加载。详见 Apple|About the App Launch Sequence

截屏2024-06-21 10.59.33.png

按文档所述,预启动不会调用UIApplication相关Delegate方法,但是发现其中有bug,情况如下:

prewarm 会触发 main() 以及 UIApplicationMain 执行,下面分是否接入UIScene来说明:

  • 对于接入 UIScene 的 App:

    • application:didFinishLaunchingWithOptions: 可能会被调用(并不总是发生)
    • scene:willConnectToSession:options: 未被调用。事实上,SceneDelegate 直到 App 打开才创建。
  • 对于没有接入 UIScene 的 App:

    • application:didFinishLaunchingWithOptions: 不会调用。

解决方案

文档素材挪用自‘师大小海腾’,其中结合了自己的bug场景以作记录📝

参考

戴铭-冷启动优化

UIScene遇到的一些问题