一、编译过程
源文件.h/.m /.cpp -> 预编译 -> 编译 ->汇编 -> 链接(动态库.a/.lib/.framwork)- 可执行文件
源文件:载入.h、.m、.cpp等文件
预处理:替换宏,删除注释,展开头文件,产生.i文件
编译:将.i文件转换为汇编语言,产生.s文件
汇编:将汇编文件转换为机器码文件,产生.o文件
链接:对.o文件中引用其他库的地方进行引用,生成最后的可执行文件
静态库:在链接阶段,会将汇编生成的目标程序与引用的库一起链接打包到可执行文件当中。此时静态库就不会在改变了,因为它是编译时 被copy一份,复制到目标程序里。
- 优点:编译完后,库文件实际上就没有作用了,目标程序没有外部依赖,直接就可以运行了
- 缺点:由于静态库会有两份,所以会导致 目标程序体积的增大 ,对内存、性能、速度消耗很大。
动态库:程序 编译时并不会链接到目标程序 中,目标程序只会存储指向动态的引用,在程序 运行时才载入。
-
优点:
1、减少了打包之后App的大小:因为不需要copy至目标程序中,所以不影响目标程序的体积,与静态库相比,减少了App 的体积大小。
2、共享内存,节约资源;同一份库可以被多个程序使用
3、通过 更新动态库,达到更新程序 的目的:由于运行时才载入的特性,可以随时对库进行替换,而不需要重写编译代码。 -
缺点:
动态库的载入会带来一部分 性能损失,使用动态库也会使得程序依赖于外部环境,如果环境缺少了动态库,或者库的版本不对,就会导致程序无法运行
二、动态链接器(dyld)
在App被编译打包成可执行文件格式的Mach-O文件后,交给dyld负责连接,加载程序。
APP启动 -> 加载libSystem ->Runtime向注册回调函数 ->加载新image -> 执行map_images Load_imags** ->调用main函数
2.png 3.png三、dyld流程分析
dyld流程分析 ->dyld_start ->dyldbootstrap::start ->dyld::_main->👇
条件准备:环境、平台、版本、路径、主机信息;
mapShareCache共享缓存加载;
instantiateFromLoadedImage实例化主程序
loadInsertedDylib加载插入的动态库
link 主程序
link 插入的动态库
weakBind 弱引用绑定主程序
initializeMainExecutable 初始化 👇
notifyMonitoringDyldMain 通知dyld可以进入main()函数
initializeMainExecutable 初始化
sMainExecutable ->runInitializers 主程序开始初始化
or
runInitializersImages开始初始化 -> processInitializers 初始化准备 -> recursiveInitialization 递归初始化👇
context.notifySingle 单个通知注入
- sNofityObjCinit调用
doInitialization 调用init方法
-registerObjCNotifiers 注册回调函数
-_dyld_objc_notify_register
- sNofityObjCinit调用
context.notifySingle 通知初始化完成
网友评论