- 冷启动:从零开始启动APP。
- 热启动:APP已经在内存中,在后台存活,再次点击图标启动APP。
启动的优化,主要是针对冷启动进行优化。
APP冷启动分为两个阶段:
pre-main阶段:即main()函数之前,操作系统加载APP可执行文件到内存,然后执行一系列的加载&链接等工作,最后执行到APP的main()函数。
main阶段:从main()开始到appDelegate的didFinishLaunchingWithOptions方法执行完毕前这段时间,主要是构建第一个界面,并完成渲染。
其中pre-main阶段会有以下过程:
dyld阶段:
装载APP的可执行(Mach-o)文件,同时会递归加载所有依赖的动态库。
当dyld把可执行文件、动态库都装载完成后,会通知Runtime进行下一步的处理。
runtime阶段:
1、调用map_images进行可执行文件内容的解析和处理
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
2、在load_images中调用call_load_methods,调用所有Class和Category的+load方法;
call_load_methods();
3、进行各种objc结构的初始化(注册Objc类、初始化类对象等等)
4、调用C++静态初始化器和_attribute_((constructor))修饰的函数
以上过程可执行文件和动态库中所有的符号(Class、Protocol、Selector、IMP、...)都已经按格式成功加载到内存中,被runtime所管理。之后就会调用main函数,然后到UIApplicationMain函数,再到appDelegate的didFinishLaunchingWithOptions。
针对pre-main的阶段,可以通过添加环境变量打印出APP的启动时间分析(Edit scheme -> Run -> Arguments -> Environment Varibles)添加DYLD_PRINT_STATISTICS并设置为1;
如果需要更详细的信息,那就将DYLD_PRINT_STATISTICS_DETAILS设置为1。
然后运行项目就可以看到过程的各项时间。
在pre-main阶段的优化有:
- 减少动态库、合并一些动态库以及定期清理不必要的动态库。
- 减少Objc类、分类的数量、减少Selector数量,也就是清理不必要的代码和类,合并功能类似的类和扩展。
- 减少C++虚函数数量
- 减少无用的静态变量
- 压缩图片等资源
- 根据情况用initiallize配合dispatch_once()来替换_attribute_((constructor))、C++静态构造器、+load方法。
针对main的阶段,因为是didFinishLaunchingWithOptions开始之后的各种代码调用了,所以需要找出耗时操作,然后对其进行相应的分析做处理,做延迟或者懒加载或者是放到子线程中执行。
可以通过instrument的Time profile工具来分析耗时。
在main阶段的优化有:
- 减少启动初始化的流程,改成懒加载、放至子线程、延迟操作等。
- 优化代码逻辑,去除不必要的逻辑和代码,减少每个流程所消耗的时间。
- 使用多线程初始化可在子线程初始化的内容。
- 首页中的UI建议使用纯代码加载,xib或者storyboard会解析成代码再去渲染,多了一些步骤。
- 不是必须的操作,可以放到第一个页面渲染完成以后viewDidAppear方法里。