main之前執(zhí)行的方法

Objective-C中在通過定義一個(gè)+load()方法,讓+load()方法在所有OC對象創(chuàng)建前被執(zhí)行,同時(shí)也會在main函數(shù)執(zhí)行前執(zhí)行。一般情況下我們使用+load()方法進(jìn)行一些hook、實(shí)現(xiàn)一些全局初始化的邏輯。

除了建立C++全局對象、實(shí)現(xiàn)OC類的+load方法來進(jìn)行一些全局的初始化邏輯外,我們還可以定義帶有特殊標(biāo)志的C函數(shù)來實(shí)現(xiàn)main函數(shù)執(zhí)行前以及main函數(shù)執(zhí)行完畢后的處理邏輯

//main函數(shù)執(zhí)行前被執(zhí)行的函數(shù)

__attribute__((constructor))

//main函數(shù)執(zhí)行完畢后被執(zhí)行的函數(shù)

__attribute__((destructor))

代碼實(shí)例
輸出結(jié)果

通過輸出結(jié)果可以確定:

1、帶有特殊標(biāo)識__attribute__((constructor))的方法會在main方法執(zhí)行前執(zhí)行;

2、帶有特殊標(biāo)識__attribute__((destructor))的方法會在main方法執(zhí)行后執(zhí)行;

main函數(shù)執(zhí)行前發(fā)生了些什么呢?

操作系統(tǒng)在啟動(dòng)一個(gè)程序時(shí),內(nèi)核會為程序創(chuàng)建一個(gè)進(jìn)程空間,并且會為進(jìn)程創(chuàng)建一個(gè)主線程,主線程會執(zhí)行各種初始化操作,完成后才開始執(zhí)行我們在程序中定義的main函數(shù)

也就是說main不是第一個(gè)執(zhí)行的函數(shù),在main之前還會執(zhí)行一系列的方法

_objc_init符號斷點(diǎn)

通過設(shè)置_objc_init符號斷點(diǎn),然后結(jié)合bt命令查看在_objc_init之前發(fā)生了哪些事情(啟動(dòng)項(xiàng)目,斷點(diǎn)到來之后,在控制臺輸入bt,會出現(xiàn)如下結(jié)果)

bt輸出內(nèi)容

看到棧底的dyldbootstrap::start()方法,繼而調(diào)用了dyld::_main()方法,其中完成了遞歸加載動(dòng)態(tài)庫過程,由于libSystem默認(rèn)引入,棧中出現(xiàn)了libSystem_initializer的初始化方法。

可以看到_objc_init調(diào)用順序,先libSystem_initializer調(diào)用libdispatch_init,再到_objc_init初始化runtime.

runtime初始化后不會閑著,在_objc_init中注冊了幾個(gè)對象,從dyld這里接手幾個(gè)活,其中包括初始化相應(yīng)依賴庫里的類結(jié)構(gòu),調(diào)用依賴庫里所有l(wèi)oad方法。

initializer方法是最后調(diào)用的,當(dāng)initializer方法被調(diào)用前dyld會通知runtime進(jìn)行類結(jié)構(gòu)初始化,然后再通知調(diào)用+load方法,這些目前都發(fā)生在main函數(shù)前,但是由于lazy bind機(jī)制,依賴庫多數(shù)都是在使用時(shí)才進(jìn)行bind,所以這些依賴庫的類結(jié)構(gòu)初始化都是發(fā)生在程序里第一次使用到該依賴庫時(shí)才進(jìn)行

dyld

系統(tǒng)先讀取App的可執(zhí)行文件(Mach-O文件),從里面獲得dyld的路徑,然后加載dyld,dyld去初始化運(yùn)行環(huán)境

1、dyld將我們可執(zhí)行文件以及插入的lib加載進(jìn)內(nèi)存,生成對應(yīng)的image

2、對上面生成的image進(jìn)行鏈接。其主要有對image進(jìn)行l(wèi)oad(加載)、rebase(基地址復(fù)位),bind(外部符號綁定)

3、這一步主要是調(diào)用所有image的initalizer方法進(jìn)行初始化。這里的initalizers方法并非名為Initalizers的方法,而是C++靜態(tài)對象初始化構(gòu)造器,atribute(constructor)進(jìn)行修飾的方法,在ImageLoader類中initializer函數(shù)指針鎖指向該初始化方法的地址

可以在程序中設(shè)置環(huán)境變量DYLD_PRINT_INITIALIZERS為1來打印出程序的各種依賴庫的initializer方法


ImageLoader

這個(gè)image不是圖片的意思,它大概表示一個(gè)二進(jìn)制文件(可執(zhí)行文件或so文件),里面是被編譯過的符號、代碼等,所以imageLoader作用是將這些文件加載進(jìn)內(nèi)存,且每一個(gè)文件對應(yīng)一個(gè)imageLoader實(shí)例來負(fù)責(zé)加載

1.在程序運(yùn)行時(shí)它先將動(dòng)態(tài)鏈接的image遞歸加載(也就是上面ImageLoader的遞歸調(diào)用)

2.再從可執(zhí)行文件image遞歸加載所有符號

runtime與load

libSystem是若干個(gè)系統(tǒng)lib的集合,所以它只是一個(gè)容器lib而已,而且它也是開源的,里面實(shí)質(zhì)上就是一個(gè)文件: init.c 由libSystem_initializer逐步調(diào)用到了_objc_init,這里就是objc和runtime的初始化入口。

除了runtime環(huán)境的初始化外,_objc_init中綁定了新image被加載后的callback

load方法加斷點(diǎn)后的調(diào)用堆棧

通過調(diào)用堆??梢郧逦闹續(xù)ain之前整體的調(diào)用次序:

1.dyld開始將程序二進(jìn)制文件初始化

2.交由imageLoader讀取image,其中包含了我們的類,方法等各種符號

3.由于runtime向dyld綁定了回調(diào),當(dāng)image加載到內(nèi)存后,dyld會通知runtime進(jìn)行處理

4.runtime接手后調(diào)用map_images做解析和處理,接下來load_images中調(diào)用call_load_methods方法,遍歷所有加載進(jìn)來的Class,按繼承層級依次調(diào)用Class的+load方法和Category的+load方法。

至此,可執(zhí)行文件中和動(dòng)態(tài)庫所有的符號(Class, Protocol,Selector,IMP,...)都已經(jīng)按格式成功加載到內(nèi)存中,被runtime所管理,再這之后,runtime的那些方法(動(dòng)態(tài)添加Class,swizzie等才能生效)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容