app的启动阶段可分为main函数调用前和main函数调用后,分别都做了些什么呢
1、pre-main 阶段
1)加载应用的可执行文件(自身App的所有.o文件的集合)
2)加载动态链接器dyld(dynamic loader,是一个专门用来加载动态链接库的库)
3)dyld递归加载应用所有依赖的动态链接库dylib
4)Bind & Rebase & Runtime 初始化
5)+load 和静态初始化
如何查看这些阶段的耗时情况呢,需要借助dyld的一些配置参数来获取一些信息。这里我们通过一个DYLD_PRINT_STATISTICS配置来获取pre-main阶段的耗时统计。
在Edit Schemes -> Run -> Arguments -> Environment Variables中添加配置:
配置完成后,运行程序看下控制台输出
可以看到main函数之前总共消耗了356.03毫秒,而且列出了每个阶段的耗时占比以及启动时最慢文件,接下来对每个阶段具体分析:
dylib loading time: 动态库加载耗时(121.68ms)。关于动态库的加载,这个是不可避免的,我们能做的就是减少动态库的引用,也可以通过合并动态库,从而减少在pre-main时的加载时间。
rebase/binding: 偏移修正/符号绑定。这个过程由操作系统完成。(ASLR安全机制,在二进制文件头部添加随机值)
ObjC setup: OC类注册以及Runtime 初始化。这也就意味着项目中OC类越多,这里消耗的时间也就会增加。
initializer: 这个阶段指的是+ (void)load,C++构造函数等初始化操作。 这里可以看到用时162.29ms,是所有项最高的。所以这里的优化比较明确:1. 能不使用+load就尽量不要使用,可以将load内部逻辑推迟到initialize时;2. 使用到了load,就尽量不要在内部执行耗时操作;3. 如果混编了C++代码,要尽量减少构造函数中的耗时操作。
可以看出main之前对于我们优化的空间不是很大。但是减少load方法的调用和针对无用代码的下线也是有一定的优化效果。另外,抖音采用的二进制重排方案也可以参考,大致原理通过Clang插桩将启动到第一帧视图展示时用到的符号放在同一张表中,这里不做介绍。
2、main阶段
1)调用main()
2)调用UIApplicationMain()
3) 调用applicationWillFinishLaunching
如何优化:
main 到 didFinishLaunching 结束或者第一个 ViewController 的 viewDidAppear 都是作为 main 之后启动时间的一个度量指标。直接使用全局变量统计打点计算即可,但遇到时间较长需要排查问题时,只有这样粗略的统计两个点的时间并不方便排查,目前比较好的方式就是为把启动任务规范化、粒子化,针对每个任务时长进行打点统计,方便后期问题的定位和优化。
第一步,在 didFinishLaunchingWithOptions 方法里,我们会创建应用的 window,指定首页视图控制器;也会由于业务需要初始化所有第三方库;检查是否需要显示引导页、是否需要登录、是否有新版本等。
第二步,首页控制器视图中的 viewWillDidLoad 中的一些操作,例如设置系统UI风格,网络请求加载数据,也会让页面加载空白时长太长。
所以综合以上两个步骤所做的工作,可以进行以下优化:
1、梳理第三方库,找到可以延迟加载的库,做延迟加载处理,比如放到首页控制器的viewDidAppear方法里。
2、梳理业务逻辑,把可以延迟执行的逻辑,做延迟执行处理。比如检查新版本、注册推送通知等逻辑。
3、避免复杂/多余的计算,另外首页控制器尽量采用纯代码方式来构建以节约耗时。
4、避免在首页控制器的viewDidLoad和viewWillAppear做太多耗时操作,因为这2个方法执行完成,首页控制器才能显示,所以部分可以延迟创建的视图应做延迟创建/懒加载处理。
通过上述两项优化后APP的启动时间基本控制在1秒左右,基本实现秒开(以首页已经出现为参考计算启动时间,真实启动加载完后比这个时间小)我们再来看下使用Xcode编辑器自带性能检测调试工具Instruments后启动时间
标签:load,启动,app,iOS,耗时,首页,main,优化,加载 From: https://blog.csdn.net/weixin_40035526/article/details/140994527