一、dyld---动态连接器
mach-o:文件格式(包括可执行文件、目标文件、静态库、动态库、dyld) 我们程序中的.m、.h、.cpp、.swift文件这些文件在生成可执行文件的之前需要经多很多过程
会经过预编译(主要是处理源代码中#开头的预编译的命令,如:#import、#include,将包含的文件插入到指定的位置,还会替换宏定义、删除注释)
编译--进行词法分析、语法分析、语义分析,会生相应的汇编代码文件
汇编--编译成机器指令,就生成.o文件
链接--静态链接和动态链接
静态库的本质是.o文件的集合
动态库是一个已经链接完全的镜像image(镜像image:用来表示任意一种类型的可执行文件,如可执行文件、dylib、bundle)
当代码经过静态链接之后不会存在静态库
1、dyld---动态连接器 app启动的时候是想从一个exec()这个函数开始的, 系统会调用exec()这个函数, 调用完这个函数就会给程序分配内存、创建进程, 然后就会把app对应的可执行文件加载进内存, 再将dyld加载到内存, dyld就会进行动态链接
2.dyld具体的工作
a.递归加载可执行文件所有依赖的动态库
b.rebase和binding
c.掉起main
3.我们通过load()方法查看堆栈信息(load()方法时在main放大之前调用的)
我们在load()方法打断点,在控制台输入bt,就能获取堆栈信息
dyld就是一个程序、也是开源的
dyld是从_dyld_start开始的,我们在dyld源码中找到_dyld_start,_dyld_start是用汇编写的,我们可以根据堆栈信息查看,他是去调用dyldbootstrap 里面start()的函数
在start()函数里面,这里是为调用main()函数做了准备,就可以调用main()函数了
进入_main()函数后,我们看下面流程图
通过流程图我们重点研究initializeMainExecutable如何初始化的
二、initializeMainExecutable初始化的
在静态库初始化之后还会为我们主可执行文件进行初始化
我们主要看runInitializers()这个方法
继续看runInitializers()这个方法processInitializers里recursiveInitialization()
这些初始化都需要依赖runtime、objc
我们一步一步找notifySingle()函数的实现,他这里走了这个分支
最后的方法就是_dyle_objc_notify_register(),但是在dyld文件中找不到这个方法的调用,我们就知道是使用了runtime的API,他就是在_objc_init()中调用的
堆栈信息调用顺序
libsystem(动态库)要在第一个初始化,libsystem里面会初始化libdispath,在libdispath里面会初始化objc_init,在objc_init会调用_dyle_objc_notify_register(&map_images,load_images, unmap_image)————这些就是dyld递归加载可执行文件依赖的动态库
libsystem -> libdispath -> objc_init -> _dyle_objc_notify_register(&map_images,load_images, unmap_image)
为什么load()方法会在main()方法之前调用,就是因为传递了load_images(镜像文件)
&map_images,load_images的详情请见下节内容