iOS底层原理 12 : 应用程序的加载
一、应用程序的加载
APP加载过程:程序启动依次加载dyld
、libSystem
、libdispathc.dyld
、libobjc动态库
,最终调用_objc_init()
方法,在此方法中Runtime向dyld
注册回调函数
,加载新的image
,执行map_images
、load_images
,imageLoader
加载image
,调用main
函数
二、dyld动态链接器
dyld(the dynamic link editor)是苹果的动态链接器,是苹果操作系统一个重要组成部分,在系统内核做好程序准备工作之后,交由dyld负责余下的工作。
1.dyld的加载过程:
1.环境变量的配置
2.共享缓存 checkSharedRegionDisable()
3.主程序的初始化
4.加入动态库 loadInsertedDylib()
5.link主程序 link(sMainExecutable, sEnv.DYLD_BIND_AT_LAUNCH, true, ImageLoader::RPathChain(NULL, NULL), -1)
6.link动态库 link(image, sEnv.DYLD_BIND_AT_LAUNCH, true, ImageLoader::RPathChain(NULL, NULL), -1)
7.initializeMainExecutable
运行所有初始化程序
8.main notifyMonitoringDyldMain()
2.reloadAllImages
加载镜像文件的步骤:
- 实例化主程序
instantiateFromLoadedImage()
,内核会映射到主要可执行文件中,我们需要为映射到主可执行文件的文件,创建ImageLoader
。在此方法中,然后读取image
,然后addImage()
读取加载镜像文件。会先在instantiateMainExecutable()
中,会确认此mach-o
文件中是否具有压缩的LINKEDIT以及段数。 - 加载插入任何动态库
loadInsertedDylib(*lib)
,将其读取为镜像文件iamge。 - 链接库。先遍历,读取image,然后link。在link中,递归插入动态加载的镜像文件。
3.initializeMainExecutable()运行所有初始化程序步骤:
runInitializers()
-
processInitializers
初始化准备。 - processInitializers中,遍历
iamge.count
,递归一个个开始初始化条件images[i]->recursiveInitialization
。 - 在递归开始初始化条件中
recursiveInitialization
,通过notifySingle
方法,对单个镜像通知开始初始化。获取镜像文件的真实地址(*sNotifyObjCInit)(image->getRealPath(), image->machHeader())
, 而notifySingle
中的sNotifyObjCInit
是在objc_init()
中注册传递过来的,所以只有当objc_init()
调用时,重新加载image
。 - notifySingle方法之后,遍历初始化
this->doInitialization(context)
- 在doInitialization方法中,先调用
doImageInit(context)
,确保libSystem库
必须提前初始化完成。再调用doModInitFunctions()
方法,对 C++和构造函数处理,然后调用libSystem_initializer
方法,调用libdispatch_init
,调用_os_object_init
,最终调用_objc_init
方法。 - _objc_init方法来注册回调函数,重新加载
images
,执行map_images
、load_images
,imageLoader
加载image
,调用main
函数。
面试题
1.main()之前系统做了哪些工作?
1)dyld 开始将程序二进制文件初始化
2)交由ImageLoader
读取 image
,其中包含了我们的类,方法等各种符号(Class、Protocol 、Selector、 IMP)
3)由于runtime
向dyld
绑定了回调,当image
加载到内存后,dyld
会通知runtime进行处理
4)runtime
接手后调用map_images
做解析和处理
5)接下来load_images
中调用call_load_methods
方法,遍历所有加载进来的Class
,按继承层次依次调用Class
的+load
和其他Category
的+load
方法
6)至此 所有的信息都被加载到内存中
7)最后dyld
发送调main函数
的通知,接下来就是main函数