因为load()
比main()
调用更早,所以咱们建立一个工程,在控制器中写一个load()
函数,并断点运行,以下图:bootstrap
运行起来以后,能够清晰的看到比较详细的函数调用顺序,从_dyld_start()
到dyld:notifySingle()
,频率出现最多的就是这个dyld,那么dyld是什么?它在作什么?缓存
简单来讲dyld是一个动态连接器,用来加载全部的库和可执行文件。接下来咱们将经过对dyld源码分析,去追踪dyld到底作了什么?markdown
dyldbootstrap::start
为关键字搜索dyldbootstrap
中调用的start()
,以下图:start
函数其中rebaseDyld()
分析以下:闭包
main
函数注:由于
dyld::main()
函数代码比较多,如下会分段介绍,也会介绍相对来讲比较重要的函数。app
// 获取CPU信息
getHostInfo(mainExecutableMH, mainExecutableSlide);
复制代码
// 设置MachHeader和内存偏移量Slide
uintptr_t result = 0;
sMainExecutableMachHeader = mainExecutableMH;
sMainExecutableSlide = mainExecutableSlide;
复制代码
// 设置上下文,保存信息
setContext(mainExecutableMH, argc, argv, envp, apple);
复制代码
// 打印环境配置信息
if ( sEnv.DYLD_PRINT_OPTS )
printOptions(argv);
if ( sEnv.DYLD_PRINT_ENV )
printEnvironmentVariables(envp);
复制代码
此处能够本身定义环境变量配置,回到刚才建立的新工程中,在Edit Scheme
-> Run
-> Arguments
-> Environment Variables
添加两个参数 DYLD_PRINT_OPTS
和 DYLD_PRINT_ENV
,并设置测试value值,以下:ide
运行程序,可看到以下打印信息:函数
主要函数mapSharedCache()
以下:oop
dyld
配置(1) dyld3
(闭包模式)源码分析
iOS11版本以后,引入dyld3闭包模式(ClosureMode):加载速度更快,效率更高。测试
开始执行闭包模式
判断是否开启了闭包模式
启动闭包模式加载
其中launchWithClosure
加载闭包,会把后面说到的dyld2
的大部分流程都封装到launchWithClosure()
这个函数里面了,这里再也不细说launchWithClosure
,由于在接下来的dyld2
(非闭包)中会详细解释整个dyld加载的流程,也就是launchWithClosure
实现过程。
(2) dyld2
(非闭包模式)
开始执行闭包模式
把dyld加入到UUID列表
// 把dyld加入到UUID列表
addDyldImageToUUIDList();
复制代码
配置缓存代理
开始建立主程序的Image,经过instantiateFromLoadedImage()
,调用instantiateMainExecutable()
,实例化具体的Image类,最后生成的对象,设置到gLinkContext
中。
// 加载完共享缓存,设置动态库的版本
checkVersionedPaths();
复制代码
// 加载插入的动态库
if ( sEnv.DYLD_INSERT_LIBRARIES != NULL ) {
for (const char* const* lib = sEnv.DYLD_INSERT_LIBRARIES; *lib != NULL; ++lib)
loadInsertedDylib(*lib);
}
复制代码
link()
函数,函数体调用了image->link()
,函数具体以下:根据Demo上的堆栈信息,以下:
终于看到熟悉的函数了,那么dyld加载流程也快结束了。 根据堆栈信息,获取函数调用层级关系。
// 初始化主程序
initializeMainExecutable();
复制代码
runInitializers()
: dyld::initializeMainExecutable()
-> ImageLoader::runInitializers()
processInitializers()
: ImageLoader::runInitializers()
-> ImageLoader::processInitializers()
recursiveInitialization()
: ImageLoader::processInitializers()
-> ImageLoader::recursiveInitialization()
notifySingle()
: ImageLoader::recursiveInitialization()
-> dyld::notifySingle()
load_images()
:在 dyld::notifySingle()
中并无找到load_images()
,可是找到了sNotifyObjCInit()
,该字段是objc函数回调。在 dyld::notifySingle()
中执行了这个回调,那就须要追溯到谁去注册的这个回调了。sNotifyObjCInit()
赋值的地方。在registerObjCNotifiers()
中赋值,以下:registerObjCNotifiers
,在_dyld_objc_notify_register()
中调用,且第二个参数是咱们须要的。以下:_dyld_objc_notify_register()
,并无在dyld源码库里找到,此时须要在源工程中,打符号断点_dyld_objc_notify_register
,从新编译执行,能够看到是_objc_init()
调用了。此时只能去查找objc源码了。objc
源码分析,在objc-os.mm
文件中找到_objc_init()
函数,其中注册了_dyld_objc_notify_register
回调。其中第二个参数就是load_images()
,在load_images()
中也找到了call_load_methods()
。
此时初始化程序还未执行完成,回到以前的 ImageLoader::recursiveInitialization()
方法中。
this->doInitialization()
函数// 通知此进程将要进入程序main()
notifyMonitoringDyldMain();
复制代码
到此,start()
-> main()
,所有执行完毕。
_dyld_start()
开始 -> dyldbootstrap::start()
main()
rebase_dyld()
dyld2
/ dyld3
(闭包模式)
loadAllImage
,主程序在第0
位置)最关键
:初始化方法initializeMainExecutable()
ImageLoader::runInitializers()
ImageLoader::processInitializers()
ImageLoader::processInitializers()
ImageLoader::recursiveInitialization()
dyld::notifySingle()
_dyld_objc_notify_register()
_objc_init()
初始化时赋值的一个函数load_images()
,里面执行了call_load_methods()
函数,其做用是循环调用各个类的方法。doModInitFunctions()
函数:内部会调用全局C++对象的构造函数 __attribute__((constructor))
的C函数main
函数