这里每天分享一个 iOS 的新知识,快来关注我吧
启动类型
在 iOS 上,我们都清楚启动类型分为"热启动"和"冷启动"。
按照苹果的定义,如果你打开 App 后滑回到主屏幕并手动杀掉程序,然后立即重新打开 App,这种启动方式通常称为"热启动"。
如果你刚刚玩了一款需要使用大量内存的游戏,你的 App 可能会被系统杀掉,并清空你的 App 所占用的内存,以允许这个游戏拥有更多内存。然后你再次进入你的应用,那么它可能会比你的平均启动速度慢得多。你的应用程序所依赖的框架和进程也可能需要重新启动和从磁盘分页。这种情况,或者在手机重启后的启动,通常被称为冷启动。
还有一种严格意义上说不叫启动,就是从后台到前台,打开 App 后,先退到后台,再点击图标打开,这种情况一般称为"恢复"(resume
)。
按照 WWDC 的这张图,可以很生动的看出,冷启动像乌龟速度最慢,热启动像兔子,速度次之,恢复像豹子,速度最快。
iOS 15 新增的启动类型
在 iOS 15 及更高版本中,系统会学习用户习惯,比如你几点经常打开某个 App,为了让你启动速度更快,在你打开之前,系统会先帮你把这个 App 进程启动并加入内存中,这就是苹果新增的一种启动类型,Prewarming Launch(预热启动)。
按照苹果的文档,预热会启动 App 进程,直到(但不包括),main()
调用 UIApplicationMain
。但是经过我的测试,预热启动不但会调用到 main 函数,甚至有时候会调用到 Appdelegate
中的 application(:, didFinishLaunchingWithOptions:)
方法。因此苹果的文档是不准确的(也可能是个 bug)。
预热启动带来的问题
虽然预热启动可以让启动速度更快,这的确提升了用户体验,但同时也带来了一些问题。
比如市面上很多 APM 工具,例如 Firebase
、Sentry
等,统计启动时间都是从进程启动到第一个页面展示出来,如果这次启动是预热启动,那么就可能导致启动时间出现非常大的误差,比如进程启动时间为早上 8 点,可能用户晚上 8 点才启动 App。
另一个问题是预热启动下有些 API 是无法正常调用的,比如钥匙串相关的 API 就无法正常调用,正如前面所说,预热启动下可能会走到 application(:, didFinishLaunchingWithOptions:)
方法,也就是说在这个方法调用的时候,之前所有访问钥匙串的代码可能都是无效的,比如你在 +load()
方法里访问了不能访问的 API,就会导致非预期的结果发生。
解决方案
这时候大家可能会想,如果可以知道这次启动是否为预热启动就好了,但是苹果并未提供相关的 API,但是国外有个 iOS 开发小哥发现苹果在环境变量中增加了一个名为"ActivePrewarm
" 的字段来标记是否为预热启动,那么我们就可以顺利使用这个标记来知道是否为预热启动了:
bash
if ProcessInfo.processInfo.environment["ActivePrewarm"] == "1" {
print("本次启动为预热启动")
} else {
print("本次启动为非预热启动")
}
可以尝试通过以下步骤测试预热启动:
-
启动你的 App
-
简单使用一会儿
-
强制退出 App
-
锁定设备,放置大概约 30 分钟
-
解锁设备
-
再次启动应用程序
以上步骤有概率复现预热启动的情况。
然后就可以根据是否为预热启动来分别解决上边提到的问题,衡量启动时间的时候可以把预热启动的情况过滤掉。
如果为预热启动,把无法正常调用的 API 放到启动成功之后。
这里每天分享一个 iOS 的新知识,快来关注我吧
本文同步自微信公众号 "iOS新知",每天准时分享一个新知识,这里只是同步,想要及时学到就来关注我吧!