本文由快学吧个人写作,以任何方法转载请表明原文出处

一、材料预备

  1. objc4-818.2

对应mac的版别是11.1。可根据自己的体系版别挑选能够进行调试的源码。

  1. dyld3-852。

由于mac的版别我没有更新过,一直停留在11.1,所以我挑选的dyld源码也是老版别的dyld3,最新版别的dyld4会在体系更新之后再单独写一篇关于dyld4的个人见解。

  1. 关于dyld和库,以及framework的一些简单的内容放在了上一章。别的,源码中有注释的当地必定是比较重要的当地,否则不会进行注释。所以自己探索的时候,有注释的当地可当作重点来看。

二、项目预备

新建一个正常的iOS的项目。

三、思路

  1. 一个常规的iOS项意图发动会从main.m中的main函数开端。而在开发中,曾发现+load办法会比main.m更先调用。
  2. 已然main函数不是最早的程序发动进口,那么+load是不是?或许说load之前是否还做过一些其他的操作?
  3. 库是怎样进入到了项目中的?dyld究竟做了什么?

四、程序的main函数之前还做了什么

  1. 在预备的项目中的ViewController里边实现+load办法。在main.m处打上断点。运转程序,运转成果如下图。

十七、app加载流程(一)dyld3

十七、app加载流程(一)dyld3

十七、app加载流程(一)dyld3

  1. 能够看到load是在main函数之前就调用了。那么load是怎样调用的?在load处打上断点。运转程序,查看仓库信息(lldb命令是bt,xcode自带查看仓库信息的当地)。

十七、app加载流程(一)dyld3

十七、app加载流程(一)dyld3

  1. 调用信息是从下向上看,所以在load之前,最开端调用的是_dyld_start

五、如何查找dyld的履行流程

  1. 打开dyld852源码,大局查找_dyld_start。只看arm64架构下的。汇编看不懂无所谓,后边有注释。

十七、app加载流程(一)dyld3

  1. 直接搜::start或许dyldbootstrap::start(是搜不到的,所以start不是一个c++的函数,那么作为一个oc的底层,最或许的便是c函数,c函数的书写规范是:void + 空格 + 函数名 + (,例如:void test()。所以搜空格 + start(

十七、app加载流程(一)dyld3

  1. dyld::main能够直接跳转到源码,直接进入dyld的main源码,这里便是dyld的主程序,也便是dyld做了什么。

六、dyld的履行流程

1. 怎样去看dyld的main函数

清晰意图 : 为了找dyld怎样加载的库,怎样就调用到了ViewController的+load办法。+load办法可是objc库中的办法,也便是在libobjc动态库中,而不是dyld中的办法。

  1. 看参数。dyld的main函数都有哪些参数,已然有这些参数,那么基本都是要用的。
  2. 有补白的一般比较重要,能够从中挑选重要的内容。
  3. 只看arm64架构下的。

2. dyld的扼要流程

1. 环境变量的处理

十七、app加载流程(一)dyld3

2. 加载同享缓存

查看同享缓存是否可用,iOS中是必须开启同享缓存的。映射同享缓存到同享区。像我们常用的UIKit、CoreFoundation框架,都是要映射加载到同享缓存区的。

十七、app加载流程(一)dyld3

3. 把dyld自己加到uuid列表中

十七、app加载流程(一)dyld3

4. 为主可履行程序实例化一个镜像加载器

说人话便是实例化一个主程序。

十七、app加载流程(一)dyld3

5. 加载一切的动态库

把一切的lib库都变成了image(镜像),在需求的时候,就能够直接map(映射)到上面。

十七、app加载流程(一)dyld3

6. 链接主可履行程序

十七、app加载流程(一)dyld3

7. 链接动态库

十七、app加载流程(一)dyld3

8. 履行弱绑定

十七、app加载流程(一)dyld3

9. 运转一切的初始化程序

十七、app加载流程(一)dyld3

10. 查找main()函数进口

十七、app加载流程(一)dyld3

11. 小结

从上面的流程能够看到,dyld是在第5步的时候,经过遍历环境变量中的DYLD_INSERT_LIBRARIES标识,把一切刺进的动态库都加载到内存中。

那么dyld是怎样调用起的objc库中的+load办法?

回看在+load处断点拿到的仓库信息 :

十七、app加载流程(一)dyld3

3. initializeMainExecutable

  1. 经过仓库信息能够看到,ViewController在调用load办法之前,是进入了dyld的main函数,然后一直到initializeMainExecutable这里,这便是上面的dyld扼要流程中的 : 9.运转一切的初始化程序。

  2. 跟着仓库信息进源码 : initializeMainExecutable —> runInitializers(需求大局查找) —> processInitializers(需求大局查找) —> recursiveInitialization(需求大局查找) —> notifySingle(需求大局查找)

十七、app加载流程(一)dyld3

  1. sNotifyObjCInit需求存储的是一个函数的地址。在这里进行回调,完结初始化程序。那么就需求找sNotifyObjCInit在哪里被赋值,被给予函数地址。大局查找sNotifyObjCInit

十七、app加载流程(一)dyld3

  1. 那么哪里调用了registerObjCNotifiers,对sNotifyObjCInit进行赋值?查找registerObjCNotifiers

十七、app加载流程(一)dyld3

  1. 找到了_dyld_objc_notify_register。再看dyld里边谁给_dyld_objc_notify_register进行了赋值,在dyld里边再就找不到相关的代码,可是在上面打印的仓库信息中能够看到,notifySingle之后的一步,履行的代码的库已经变成了libobjc

十七、app加载流程(一)dyld3

  1. 已然是libobjc,那么上面预备好的项目便是能够使用的。在上面预备好的项目中打一个_dyld_objc_notify_register的符号断点,再运转程序看看哪里调用的_dyld_objc_notify_register

十七、app加载流程(一)dyld3

  1. 找到objc4-818.2的源码,查找_dyld_objc_notify_register

十七、app加载流程(一)dyld3

  1. 发现进入了objc库的初始化函数。而dyld中的sNotifyObjCInit存储的是load_images函数的地址,也便是调用sNotifyObjCInit就等于调用load_images

  2. 在查看_dyld_objc_notify_register断点的仓库信息的时候,也能够根据仓库信息得到一个定论 : 运转一切库的初始化也是有次序的,仓库信息从下向上看,便是调用次序,先是libSystem初始化、然后是libdispatch初始化,然后是libobjc初始化。

十七、app加载流程(一)dyld3

七、总结

  1. dyld的进口是 : _dyld_start_dyld_start会调用dyldbootstrap::start函数。这不是一个c++函数,而是一个C函数。
  2. start函数会返回dyld::main,也便是dyld的主函数。也便是_dyld_start会调起dyld的main函数。
  3. dyld的main函数的首要流程 :

(1). 环境变量的查看和处理。

(2). 查看同享缓存是否可用(iOS环境下强制可用),加载同享缓存,并将同享缓存映射到同享区。

(3). dyld将自己加入到UUID列表中。

(4). 为主可履行程序初始化一个镜像加载器。便是实例化这个主程序。

(5). 加载一切的动态库。便是将一切的动态库都变成镜像,在需求的当地,直接能够映射。

(6). 链接主可履行程序。

(7). 链接一切的动态库。链接动态库必定要在链接主可履行程序之后履行,这是为了保证主可履行程序的动态库的优先性,其他刺进的动态库必须在主可履行程序的动态库的后边。

(8). 对主可履行程序履行弱绑定。

(9). 履行一切初始化函数。

(10). 找main()函数的进口

  1. 在履行一切的初始化函数的时候,libSystem、libDispatch,libobjc,就会进行初始化,并且是依照libSystem —> libDispatch —> libobjc这个次序初始化的。

  2. dyld和libobjc的联系是经过告诉来完结的,实质上是经过c的函数回调方法。