iOS開發(fā)-APP啟動main()調用之前的加載過程

main()調用之前的加載過程

App開始啟動后焚志,系統(tǒng)首先加載可執(zhí)行文件(自身App的所有.o文件的集合)芜赌,然后加載動態(tài)鏈接庫dyld勉盅。
dyld是一個專門用來加載動態(tài)鏈接庫的庫哼鬓。
dyld源碼鏈接
執(zhí)行從dyld開始送浊,dyld從可執(zhí)行文件的依賴開始, 遞歸加載所有的依賴動態(tài)鏈接庫青伤。

動態(tài)鏈接庫包括:
iOS 中用到的所有系統(tǒng) framework
加載OC runtime方法的libobjc督怜,
系統(tǒng)級別的libSystem,例如libdispatch(GCD)和libsystem_blocks (Block)狠角。

其實無論對于系統(tǒng)的動態(tài)鏈接庫還是對于App本身的可執(zhí)行文件而言号杠,他們都算是image(鏡像),而每個App都是以image(鏡像)為單位進行加載的。

什么是image(鏡像)

1.executable可執(zhí)行文件 比如.o文件姨蟋。
2.dylib 動態(tài)鏈接庫 framework就是動態(tài)鏈接庫和相應資源包含在一起的一個文件夾結構屉凯。
3.bundle 資源文件 只能用dlopen加載,不推薦使用這種方式加載眼溶。

除了我們App本身的可行性文件悠砚,系統(tǒng)中所有的framework比如UIKit、Foundation等都是以動態(tài)鏈接庫的方式集成進App中的堂飞。

系統(tǒng)使用動態(tài)鏈接有幾點好處:

代碼共用:很多程序都動態(tài)鏈接了這些 lib灌旧,但它們在內存和磁盤中中只有一份。 易于維護:由于被依賴的 lib 是程序執(zhí)行時才鏈接的绰筛,所以這些 lib 很容易做更新枢泰,比如libSystem.dylib 是 libSystem.B.dylib 的替身,哪天想升級直接換成libSystem.C.dylib 然后再替換替身就行了铝噩。 減少可執(zhí)行文件體積:相比靜態(tài)鏈接衡蚂,動態(tài)鏈接在編譯時不需要打進去,所以可執(zhí)行文件的體積要小很多骏庸。


Mach-O鏡像加載.png

如上圖所示毛甲,不同進程之間共用系統(tǒng)dylib的_TEXT區(qū),但是各自維護對應的_DATA區(qū)具被。

所有動態(tài)鏈接庫和我們App中的靜態(tài)庫.a和所有類文件編譯后的.o文件最終都是由dyld(the dynamic link editor)玻募,Apple的動態(tài)鏈接器來加載到內存中。每個image都是由一個叫做ImageLoader的類來負責加載(一一對應)硬猫,那么ImageLoader又是什么呢补箍?

什么是ImageLoader

image 表示一個二進制文件(可執(zhí)行文件或 so 文件),里面是被編譯過的符號啸蜜、代碼等坑雅,所以 ImageLoader 作用是將這些文件加載進內存,且每一個文件對應一個ImageLoader實例來負責加載衬横。
兩步走: 在程序運行時它先將動態(tài)鏈接的 image 遞歸加載 (也就是上面測試棧中一串的遞歸調用的時刻)裹粤。 再從可執(zhí)行文件 image 遞歸加載所有符號。

當然所有這些都發(fā)生在我們真正的main函數(shù)執(zhí)行前蜂林。

動態(tài)鏈接庫加載的具體流程

動態(tài)鏈接庫的加載步驟具體分為5步:

1.load dylibs image 讀取庫鏡像文件
2.Rebase image
3.Bind image
4.Objc setup
5.initializers

1.load dylibs image

在每個動態(tài)庫的加載過程中遥诉, dyld需要:
1.分析所依賴的動態(tài)庫
2.找到動態(tài)庫的mach-o文件
3.打開文件
4.驗證文件
5.在系統(tǒng)核心注冊文件簽名
6.對動態(tài)庫的每一個segment調用mmap()
通常的,一個App需要加載100到400個dylibs噪叙, 但是其中的系統(tǒng)庫被優(yōu)化矮锈,可以很快的加載。

針對這一步驟的優(yōu)化有:
1.減少非系統(tǒng)庫的依賴
2.合并非系統(tǒng)庫
3.使用靜態(tài)資源睁蕾,比如把代碼加入主程序

2.rebase/bind

由于ASLR(address space layout randomization)的存在苞笨,可執(zhí)行文件和動態(tài)鏈接庫在虛擬內存中的加載地址每次啟動都不固定债朵,所以需要這2步來修復鏡像中的資源指針,來指向正確的地址瀑凝。

rebase修復的是指向當前鏡像內部的資源指針序芦;
而bind指向的是鏡像外部的資源指針。

rebase步驟先進行粤咪,需要把鏡像讀入內存谚中,并以page為單位進行加密驗證,保證不會被篡改寥枝,所以這一步的瓶頸在IO宪塔。
bind在其后進行,由于要查詢符號表脉顿,來指向跨鏡像的資源蝌麸,加上在rebase階段,鏡像已被讀入和加密驗證艾疟,所以這一步的瓶頸在于CPU計算。

//通過命令行可以查看相關的資源指針:
xcrun dyldinfo -rebase -bind -lazy_bind myApp.App/myApp

優(yōu)化該階段的關鍵在于減少__DATA segment中的指針數(shù)量敢辩。

可以優(yōu)化的點有:

1.減少Objc類數(shù)量蔽莱, 減少selector數(shù)量
2.減少C++虛函數(shù)數(shù)量
3.轉而使用swift stuct(其實本質上就是為了減少符號的數(shù)量)

3.Objc setup

這一步主要工作是:
1.注冊Objc類 (class registration)
2.把category的定義插入方法列表 (category registration)
3.保證每一個selector唯一 (selctor uniquing)
4.由于之前2步驟的優(yōu)化,這一步實際上沒有什么可做的戚长。

4.initializers

以上三步屬于靜態(tài)調整(fix-up)盗冷,都是在修改__DATA segment中的內容,而這里則開始動態(tài)調整同廉,開始在堆和堆棧中寫入內容仪糖。 在這里的工作有:

1.Objc的+load()函數(shù)
2.C++的構造函數(shù)屬性函數(shù) 形如attribute((constructor)) void DoSomeInitializationWork()
3.非基本類型的C++靜態(tài)全局變量的創(chuàng)建(通常是類或結構體)(non-trivial initializer) 比如一個全局靜態(tài)結構體的構建,如果在構造函數(shù)中有繁重的工作迫肖,那么會拖慢啟動速度
Objc的load函數(shù)和C++的靜態(tài)構造函數(shù)采用由底向上的方式執(zhí)行锅劝,來保證每個執(zhí)行的方法,都可以找到所依賴的動態(tài)庫蟆湖。

1).dyld 開始將程序二進制文件初始化
2).交由 ImageLoader 讀取 image故爵,其中包含了我們的類、方法等各種符號
3).由于 runtime 向 dyld 綁定了回調隅津,當 image 加載到內存后诬垂,dyld 會通知 runtime 進行處理
4).runtime 接手后調用 mapimages 做解析和處理,接下來 loadimages 中調用 callloadmethods 方法伦仍,遍歷所有加載進來的 Class结窘,按繼承層級依次調用 Class 的 +load 方法和其 Category 的 +load 方法

至此
至此,可執(zhí)行文件中和動態(tài)庫所有的符號(Class充蓝,Protocol隧枫,Selector,IMP,…)都已經(jīng)按格式成功加載到內存中悠垛,被 runtime 所管理线定,再這之后,runtime 的那些方法(動態(tài)添加 Class确买、swizzle 等等才能生效)斤讥。

整個事件由 dyld 主導,完成運行環(huán)境的初始化后湾趾,配合 ImageLoader 將二進制文件按格式加載到內存芭商, 動態(tài)鏈接依賴庫,并由 runtime 負責加載成 objc 定義的結構搀缠,所有初始化工作結束后铛楣,dyld 調用真正的 main 函數(shù)。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末艺普,一起剝皮案震驚了整個濱河市簸州,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌歧譬,老刑警劉巖岸浑,帶你破解...
    沈念sama閱讀 210,978評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異瑰步,居然都是意外死亡矢洲,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評論 2 384
  • 文/潘曉璐 我一進店門缩焦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來读虏,“玉大人,你說我怎么就攤上這事袁滥「乔牛” “怎么了?”我有些...
    開封第一講書人閱讀 156,623評論 0 345
  • 文/不壞的土叔 我叫張陵呻拌,是天一觀的道長葱轩。 經(jīng)常有香客問我,道長藐握,這世上最難降的妖魔是什么靴拱? 我笑而不...
    開封第一講書人閱讀 56,324評論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮猾普,結果婚禮上袜炕,老公的妹妹穿的比我還像新娘。我一直安慰自己初家,他們只是感情好偎窘,可當我...
    茶點故事閱讀 65,390評論 5 384
  • 文/花漫 我一把揭開白布乌助。 她就那樣靜靜地躺著,像睡著了一般陌知。 火紅的嫁衣襯著肌膚如雪他托。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,741評論 1 289
  • 那天仆葡,我揣著相機與錄音赏参,去河邊找鬼。 笑死沿盅,一個胖子當著我的面吹牛把篓,可吹牛的內容都是我干的。 我是一名探鬼主播腰涧,決...
    沈念sama閱讀 38,892評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼韧掩,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了窖铡?” 一聲冷哼從身側響起疗锐,我...
    開封第一講書人閱讀 37,655評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎万伤,沒想到半個月后窒悔,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,104評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡敌买,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了阶界。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片虹钮。...
    茶點故事閱讀 38,569評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖膘融,靈堂內的尸體忽然破棺而出芙粱,到底是詐尸還是另有隱情,我是刑警寧澤氧映,帶...
    沈念sama閱讀 34,254評論 4 328
  • 正文 年R本政府宣布春畔,位于F島的核電站,受9級特大地震影響岛都,放射性物質發(fā)生泄漏律姨。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,834評論 3 312
  • 文/蒙蒙 一臼疫、第九天 我趴在偏房一處隱蔽的房頂上張望择份。 院中可真熱鬧,春花似錦烫堤、人聲如沸荣赶。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽拔创。三九已至利诺,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間剩燥,已是汗流浹背慢逾。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留躏吊,地道東北人氛改。 一個月前我還...
    沈念sama閱讀 46,260評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像比伏,于是被迫代替她去往敵國和親胜卤。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,446評論 2 348

推薦閱讀更多精彩內容