本文尽量简化内容,主写启动优化答题思路,一些相关知识尽量写其它文章来说明。
优化思路
- 整理启动流程
- 监控各阶段耗时
- 解决各阶段耗时问题
iOS启动流程
概述
分为 premain() 和 main() 两个阶段
premain()
- Load dylibs (装载、读取 image)
- Rebase、Rebind (链接)
- Objc (处理一些OC动态特性,包括但不限于以下几点)
- oc类注册,
- 将分类信息添加至相关宿主类
- selector 唯一性检查
- Intializers (各种需要初始化的操作,包括但不限于以下几点)
- C++编译器生成初始化器来完成那些抽象DATA的初始化。
- objc 中的非懒加载的类的load方法(父类先于子类、主类先于分类)
- 所有 dylib 的初始化器
- 创建 c++ 全局静态变量 (对于 c++ 的内存布局不是很了解,等复盘完iOS相关后再往下研究)
main()
如果项目中已经应用SceneDelegate 那么流程自然会有相应的变化。 以下是一般流程描述:
- dyld调用main()
- UIApplicationMain()
- applicationWillFinishLaunching
- didFinishLaunchingWithOptions
监控耗时
理论大体是上述所述,想要项目实操,发现问题才是第一步。
时间检测的意义
- 检测 premain、main()的时间 目的主要是为了调试,修复问题。
- 线上监控 整体启动时间,目的是为了实时检测,监测每个迭代功能更新对于启动时间的影响。 个人认为,在2021年这个时间点,网上找资料、借鉴前辈们的经验,面向优化点解决问题是不难的。那么就要更多的思考怎么杜绝此类问题的出现,真正的面向工程解决问题。
检测 premain 时间
当前 scheme 环境变凉添加 DYLD_PRINT_STATISTICS
输出结果为
检测 main() 时间
- 配置工程在debug模式下输出符号表
- 选择 instruments 中的 App Launch。 运行大约5秒后会关闭程序并显示详细的启动过程
可以看到启动流程的各个阶段, 可以框选 didFinishLaunchingWithOptions()阶段,分析业务函数调用链。
线上监控
线上监控,是启动优化非常重要的一环。没有线上数据的监控,只会一次次重复问题排查的苦力活。 目前了解到的线上监控系统有,Metrics(美团)、字节跳动 目前负责的App和大厂超级App没法比,暂时使用比较简单的嵌入式代码方案。 未完待续
解决方案
根据上文,列举一些可以优化的点
解决方案(premain)
- Load dylibs : 减少 dylib 数量,可以转为静态库的尽量转换为静态库, 减少bundler 资源数量
- Rebase/Bind : 减少无用类、无用函数、无用分类.
- Objc setup :
- Initializers : 减少 load 方法耗时,减少 c++ 静态全局变量
解决方案(main)
- 通过 instruments 发现耗时严重方法,选择后台线程进行或者等待首帧页面出现后调用。
- 二进制重排 未完待续
产品层面
未完待续