iOS 性能优化 启动
iOS系统启动时间
app总的启动时间 = t1(main()之前的加载时间) + t2(mian()之后的加载时间)
t1 = 系统dylib(动态链接库)和自身App可执行文件的加载。
t2 = main方法执行之后到didFinishLaunchingWithOptions执行结束前的这段时间。
查看pre-Main执行时间
对于pre-main阶段,Apple提供了一种测量方法,在 Xcode 中 Edit scheme -> Run -> Auguments 将环境变量DYLD_PRINT_STATISTICS 设为1 。
解释几个词
Mach-O文件//image
- Executable:应用的主要二进制,比如.o文件。;
- Dylib Library:动态链接库;
- Static Library:静态链接库;
- Bundle:不能被链接的Dylib,只能在运行时使用dlopen( )加载,可当做macOS的插件;
- Relocatable Object File :可重定向文件类型。
imageLoader
image表示一个二进制文件,里面是被编译过的符号、代码等,所以 imageLoader将这些文件加载到内存,并且每一个文件对应一个 imageLoder实例进行加载。
动态链接库加载的流程
- load dylibs image 读取镜像文件
- Rebase image
- Bind image
- Objec setup
- initializers
1、load dylibs image
- 分析所依赖的动态库
- 找到动态库的mach-o文件
- 打开文件
- 验证文件
- 在系统核心注册文件签名
- 对动态库的每一个segment调用mmap()
mmap概念:将一个文件或者其它对象映射进内存
ASLR (Address Space Layout Randomization),即地址空间随机布局
2、rebase/bind
由于ASLR的存在,可执行文件和动态链接库在虚拟内存中的加载地址每次启动都不固定,所以需要这一步来进行image中的指针修改,来指向正取的地址。rebase修复当前image内部的资源指针;bind指向的是image外部的资源指针。
3、Objc setup
- 注册Objc类(class registration)
- 把category的定义方法插入到方法列表 (category registration)
- 保证每一个selector唯一(selector uniquing)
4、initializers
- Objc的+load()函数
- C++的构造函数属性函数
- 非基本类型的C++静态全局变量的创建(通常是类或结构体)(non-trivial initializer)
总结一下:对于main()调用之前的耗时我们可以优化的点有:
减少不必要的framework,因为动态链接比较耗时
check framework应当设为optional和required,如果该framework在当前App支持的所有iOS系统版本都存在,那么就设为required,否则就设为optional,因为optional会有些额外的检查
合并或者删减一些OC类,关于清理项目中没用到的类,使用工具AppCode代码检查功能,查到当前项目中没有用到的类如下:
LinkMap
LinkMap文件是Xcode产生可执行文件的同时生成的链接信息,用来描述可执行文件的构造成分,包括代码段(__TEXT)和数据段(__DATA)的分布情况。
在Xcode中,选择XCode -> Target -> Build Settings -> 搜map -> 把Write Link Map File选项设为YES,并指定好linkMap的存储位置.
动态链接库共享缓存
当构建一个程序时,将会链接各种各样的库。它们又会依赖其他一些 framework 和 动态库。需要加载的动态库会非常多。而对于相互依赖的符号就更多了。可能将会有上千个符号需要解析处理,这将花费很长的时间:一般是好几秒钟。
为了缩短这个处理过程所花费时间,在 OS X 和 iOS 上的动态链接器使用了共享缓存,共享缓存存于 /var/db/dyld/。对于每一种架构,操作系统都有一个单独的文件,文件中包含了绝大多数的动态库,这些库都已经链接为一个文件,并且已经处理好了它们之间的符号关系。当加载一个 Mach-O 文件 (一个可执行文件或者一个库) 时,动态链接器首先会检查 共享缓存 看看是否存在其中,如果存在,那么就直接从共享缓存中拿出来使用。每一个进程都把这个共享缓存映射到了自己的地址空间中。这个方法大大优化了 OS X 和 iOS 上程序的启动时间。
这里也就引入了冷启动和热启动的概念。
编译选项优化
- Build Settings->Optimization Level:release版应该选择Fastest, Smalllest,这个选项会开启那些不增加代码大小的全部优化,并让可执行文件尽可能小。
- Build Settings->Strip Debug Symbols During Copy: release版应该设置为YES,可以去除不必要的调试符号。
- Build Settings->Symbols Hidden by Default:release版应该设置为YES,会把所有符号都定义成”private extern”。