关于mach-o
mach-O文件为Mach Object
文件格式的缩写,它是一种用于可执行文件,目标代码,动态库,内核转储的文件格式。
常见的有以下形式:
- Executable 可执行文件
- Dylib动态库和Framework动态库,对应头文件和资源文件集合
Apple可执行文件格式几乎都是mach-o;
关于更多mach-o ,可参考Mac OS X ABI Mach-O File Format Reference
关于mach-o
数据结构,可参考Mac本地路径下的/usr/include/mach-o
源码。
为了直观看出mach-o
相关信息,可以使用三方工具MachOView
MachOView下载
编译好的工程很老了,建议下载源码自己运行使用。
MachOView源码地址
关于安装源码启动报错,可参看别人已经写好的说明,我这边就不多说了。
mach-o文件分析工具 MachOview探究
这里我用MachOView工具打开了我本地的一个动态库。如图:
关于 dyld
dyld(the dynamic link editor)是苹果的动态链接器,在系统内核做好程序准备工作之后,交由dyld负责余下的工作。它代码是开源的。源码地址。
在App启动时它就负责加载mach-o文件。
关于 dyld详细解析说明,可参考dyld详解
App 具体启动流程
App启动一般分两种冷启动和热启动:
冷启动是指, App 点击启动前,它的进程不在系统里,需要系统新创建一个进程分配给它启动的情况。这是一次完整的启动过程。
热启动是指 ,App 在冷启动后用户将 App 退后台,在App 的进程还在系统缓存的情况下,用户重新启动进入 App。
这里我们说的主要是冷启动。
App启动主要包括三个阶段:
1: main()函数执行前
这里就主要是dyld加载mach-o文件了
加载步骤主要分以下几步:
- 设置运行环境
主要设置运行参数,环境变量之类的。可以再xcode的 Product->Scheme -> EditScheme,配置环境变量DYLD_PRINT_OPTS
和DYLD_PRINT_ENV
,通过xcode打印dyld加载各种运行参数和环境变量。
- 加载共享缓存
如果共享缓存已加载就不在额外处理。
加载可执行文件
主程序的
Mach-O
加载进内存,并实例化一个ImageLoader
。
ImageLoader
是抽象类,其子类负责把Mach-O
文件实例化。-
加载动态库
会先从共享缓存中搜索
链接主程序,进行 rebase 指针调整
将实例化后的主程序进行动态修正,让二进制变为可正常执行的状态。
Rebase 修正内部(指向当前mach-o文件)的指针指向.Bind 修正外部指针指向
包括链接插入的动态库,执行弱符号绑定。运行时Runtime 初始化
相关类注册
,分类注册
,方法唯一性检查.
我们可以通过先加符号断点 断在_objc_init
,就可清晰看到dyld
执行到runtime
初始化
之前的调用了。
我们可以看到:
栈底的dyldbootstrap::start()
方法,继而调用了dyld::_main()
方法,再到ImageLoader
,再到objc_init
。
其他必要的初始化
+load方法,
C/C++静态初始化对象和标记为attribute(constructor)的方法
2:main()函数执行后
从main()函数执行开始,到appDelegate 的 didFinishLaunchion里首屏渲染相关方法执行完成。
3:首屏渲染完成之后
其他业务模块相关代码初始化,文件处理,业务监听等等。
App启动时间优化
在Xcode中,可以通过设置环境变量来查看App的启动时间,DYLD_PRINT_STATISTICS
和DYLD_PRINT_STATISTICS_DETAILS
。
通过了解App启动流程,我们可以做以下优化
main()函数执行前:
减少动态库数量,可以合并动态库
减少无用的方法和类,合并分类
减少 在load方法,试着用Initialize替代
减少atribute((constructor))的使用,控制全局变量数量
main()函数之后:
对业务进行模块化处理,非首屏渲染业务应当尽量放到首屏渲染之后处理。