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函数