补充(新):
下边主要 讲的是怎么看你到底哪里耗时的地方比较多,看起来比较繁杂,我这里做一个简单的总结
- 对于我们App启动分为 冷启动 热启动, 区别在于你的App 进程有没有在系统中
- 然后对于启动我们有三个时刻 main()前 --- main()后 --- 首屏渲染完成
-
main()前主要可执行文件加载, 动态库链接, Objc 运行时的处理, 初始化一些方法+load() attribute((constructor)), C++静态全局变量;
-
mian()后 首屏初始化需要的 文件读写,数据获取,渲染计算;
-
首屏完成后 非首屏必要的业务模块初始化,监听注册,配置文件读取
-
-
- 对应干了什么有对应解决方法:
-
文件瘦身,动态库减少/合并,减少不适用的类和方法 +load()之后或者换成+initialize(), C++静态全局变量数量减少
-
必要的初始化才做,不必要的要延迟或者放到对应的位置
-
-
- 总结两点:
-
a. 功能级别的优化 在main()后到首屏渲染结束前 我们只处理首屏相关的业务
-
b. 方法级别的优化 主线程上耗时的 异步执行或者滞后执行(加载编辑 存储图片 文件等资源)
-
+load()方法 4ms ReactiveCocoa框架创建一个信号耗时6ms 这些方法都需要进行处理 滞后或者用对应的方法代替
-
前言:
在优化App启动速度的前提是知道自己的App
1.从启动 main()之前
2.main()之后 到didFinishLaunchingWithOptions , 首屏渲染结束
3.以及didFinishLaunchingWithOptions 作用域结束
这些过程的耗时,才能知道自己到底哪个过程出了问题.
操作:
Time Profiler:
那我们通过什么工具来分析自己App 的启动速度呢? 大部分会想到Time Profiler那我们先用这个来打点记录测试一下App的耗时时间
Separate By Thread:线程分离
Invert Call Tree:从上到下跟踪堆栈信息
Hide System Libraries:隐藏系统的方法。
一般勾选这些 更清楚的看到自己代码的问题 ,能看到调用最深的耗时方法 可以看到 App启动的时候 大概需要的时间是 5s 和3s 左右.
这里我说明一下:
App 启动 分为<冷启动>和<热启动>
冷启动: 是App没有打开.没有做任何准备工作没有进行应用的加载以及构建的时候启动到首屏渲染完成的时间.(我们的App 在这个阶段分为两种首次安装打开以及App不在后台挂起的情况下kill 之后重新打开, 因为做了不同配置导致耗时不同).
热启动: 是你的App已经运行但是是在后台挂起的状态.热启动的时候的时间38ms 个人觉得我们主要优化的应该是冷启动的启动时间
App启动流程:
第一种划分
App 启动的顺序: 可以直接去苹果开发者文档中看 About the App Launch Sequence
重点提出其中几点:
截了一个顺序的图:
1.pre-main :
在call main()之前 系统会进行Parse images -> Map images -> Rebase images -> Bind images -> Run image initializers(images 镜像 二进制文件 dylib动态链接库 bundle资源文件).1. 加载可执行文件 2.加载动态链接器dyld 3.dyld递归加载应用所有依赖的动态链接库dylib.
2. main() :
dyld 调用main() -> UIApplicationMain() -> applicationWillFinishLaunching -> didFinishLaunchingWithOptions
第二种划分:
我们主要是进行对于冷启动的优化
冷启动优化的过程大概分为3个阶段(跟上边的划分方式有交叉,其实都是一样 划分的方式不同而已):
a. dyld b. runtime c. main (下方图片 从左到右)
1.dyld (动态连接器) :
内核加载主程序,dyld装载Mach-O文件 管理iamges 加载动态库(以后对dyld 在进行深入了解)
装载App可执行文件,递归(动态库依赖于其他动态库)加载所有依赖的动态库;
dyld 把可执行文件以及动态库都装载完之后,通知Runtime进行下一步处理;
2.Runtime (初始化OC结构) :
(这个过程可以将可执行文件和动态库中的所有的符号Class, Protocol, Selecor, IMP等按格式成功加载到内存 中且Runtime对其进行管理,面向对象的相关内容都是Runtime 做的)
调用map_images 进行可执行文件内容的解析和处理
objc4 源码中 在objc_init 里进行了一系列操作:
void _objc_init(void) { static bool initialized = false; if (initialized) return; initialized = true; // fixme defer initialization until an objc-using image is found? environ_init(); tls_init(); static_init(); lock_init(); exception_init(); _dyld_objc_notify_register(&map_images, load_images, unmap_image); }
其中就有map_images
map_images(unsigned count, const char * const paths[], const struct mach_header * const mhdrs[]) { rwlock_writer_t lock(runtimeLock); return map_images_nolock(count, paths,