前言
- WWDC 2016 中首次出现了 App 启动优化的话题,其中提到:
App 启动最佳速度是
400ms以内,因为从点击 App 图标启动,然后 Launch Screen 出现再消失的时间就是400ms;
App 启动最慢不得大于20s,否则进程会被系统杀死;(启动时间最好以 App 所支持的最低配置设备为准。) - APP的启动可以分为2种
冷启动(Cold Launch):从零开始启动APP
热启动(Warm Launch):APP已经在内存中,在后台存活着,再次点击图标启动APP - APP启动时间的优化,主要是针对冷启动进行优化
监测启动耗时
- premain阶段
通过添加环境变量可以打印出APP的启动时间分析(Edit scheme -> Run -> Arguments)DYLD_PRINT_STATISTICS设置为1普通打印
DYLD_PRINT_STATISTICS_DETAILS设置为1详细打印%E5%90%AF%E5%8A%A8%E4%BC%98%E5%8C%96%E6%B1%87%E6%80%BB%E7%AF%87/DYLD_PRINT_STATISTICS.jpg)
- main函数阶段
我们可以通过Instruments的TimeProfile来统计启动时的主要方法耗时,点击左上角红色按钮运行,勾选左下角Call Tree中Separate Thread和Hide System Libraries过滤掉系统库可以查看主线程下方法的耗时。%E5%90%AF%E5%8A%A8%E4%BC%98%E5%8C%96%E6%B1%87%E6%80%BB%E7%AF%87/timeProfile.jpg)
启动流程
%E5%90%AF%E5%8A%A8%E4%BC%98%E5%8C%96%E6%B1%87%E6%80%BB%E7%AF%87/launch.jpg)
- premian函数之前
- dylib loading time
动态库加载时间,苹果推荐我们动态库不要超过6个,如果大于6个,多个动态库合并。所以减少动态库的使用,有助于优化启动时间。
- rebase/binding
rebase:程序加载到内存中都会随机的分配一个基地址,这个地址我们称之为
ASLR,每一个地址都会加上这个ASLR,才是真正在内存中运行的地址。
binding:应用程序可能会用到外部的符号,比如NSLOG等方法。第一次调用该方法的时候能通过符号去找指针,并绑定。第二次访问的时候就是真正的地址。 - Objc setup time
OC类的注册。在这个过程中主要做了一些耗时的操作
读取macho data,找到oc相关的信息
注册oc类的时候,会有一张全局的映射表,里面存储了类名和类映射关系,sel和 imp一些相关的信息。可能还有一些分类。 - initializer time
会执行+load方法,c++构造函数,用时。所以在项目中,我们不要在这些方法里面做一些延迟加载的事情。
- dylib loading time
启动优化方案
- 使Cpu性能最大化,异步处理,在不影响用户体验的前提下,尽可能将一些操作延迟,不要全部都放在
didFinishLaunching方法中, - 自定义动态库不要超过6个,可以合并动态库或使用静态库
- 清理不必要的类、分类,
Swift尽量使用struct,减少C++虚函数数量-
使用fui找到没有#import的代码
//安装 fui 工具 在终端中执行命令
sudo gem install fui -n /usr/local/bin
fui usage: https://github.com/dblock/fui
到工程目录下,执行 fui find 命令,可以找出所有的没有用到的class文件 -
利用AppCode(收费,用破解版) 检测未使用的代码:菜单栏 -> Code -> Inspect Code
-
- 用
+initialize方法和dispatch_once取代所有的__attribute__((constructor))、C++静态构造器、ObjC的+load - 二进制重排,详见下篇文章 启动优化(2)二进制重排