因爲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
函數