iOS 優(yōu)化啟動(dòng)時(shí)間

更多內(nèi)容請(qǐng)挪步我的博客

前言

啟動(dòng)時(shí)間包括 main 執(zhí)行之前的時(shí)間竖般,以及 main 執(zhí)行之后的時(shí)間。

main 執(zhí)行之后的時(shí)間通過(guò) Time Profiler 查看即可利花,之前寫過(guò)文章撇寞,詳情點(diǎn)擊這里

這篇文章主要討論 main 執(zhí)行之前的時(shí)間摹迷。

main 之前 的加載過(guò)程

Load dylibs => Rebase => Binding => ObjC => Initializers

在 Xcode 中 Edit scheme -> Run -> Auguments 中設(shè)置 DYLD_PRINT_STATICS 環(huán)境變量可以輸出 main 之前執(zhí)行的時(shí)間

Load dylibs

要了解第一個(gè)階段的 dylibs见妒,先要介紹下 OS X 的可執(zhí)行文件 Mach-O

Mach-O 二進(jìn)制格式

系統(tǒng)判斷一個(gè)文件是否可以執(zhí)行孤荣,是通過(guò)將文件讀入內(nèi)存,然后尋找一個(gè)頭簽名须揣,頭簽名通常被稱為 魔數(shù) magic,通過(guò) magic 可以判斷文件的二進(jìn)制格式钱豁,如果是被支持的二進(jìn)制格式耻卡,那么就可以執(zhí)行該文件。

在很多種可執(zhí)行文件格式中牲尺, OS X 目前只支持:解釋器腳本格式卵酪、通用二進(jìn)制格式以及 Mach-O 格式。Mach-Object谤碳,簡(jiǎn)稱 Mach-O溃卡,是蘋果在 OS X 中維護(hù)的一種獨(dú)有的二進(jìn)制格式。

Mach-O 的文件頭 mach_header 中包含magic蜒简、CPU 類型和子類型等瘸羡。mach_header 其后包含了很多指令,這些指令被調(diào)用時(shí)清晰地指導(dǎo)了如何設(shè)置并加載二進(jìn)制數(shù)據(jù)搓茬,這些指令被稱為“加載指令”犹赖。加載指令包括將文件中的段映射到進(jìn)程地址空間队他、調(diào)用 dyld、開啟線程峻村、代碼簽名等麸折。

OS X 上幾乎所有的程序都是動(dòng)態(tài)鏈接的,僅有非常少量的進(jìn)程只需要內(nèi)核加載起就可以完成加載粘昨。通常情況下垢啼,使用 dyld 作為動(dòng)態(tài)連接器。

Loading Dylibs 的加載過(guò)程又分為

Parse list of dependent dylibs => Find requested mach-o file => Open and read start of file => Validate mach-o => Register code signature => Call mmap() for each segment

上面說(shuō)過(guò) mach_header 中包含很多指令张肾,其中包含了 LC_CODE_SIGNATURE 用于數(shù)字簽名膊夹,iOS 強(qiáng)制要求代碼簽名,且代碼簽名和沙盒機(jī)制是綁定在一起的捌浩,也就是說(shuō)必須放到沙盒中經(jīng)過(guò)簽名才能運(yùn)行放刨,非越獄的機(jī)器無(wú)法自行下載一個(gè)動(dòng)態(tài)庫(kù)并執(zhí)行。

mmap的作用是將一個(gè)文件或者其他對(duì)象映射進(jìn)內(nèi)存尸饺,普通文件被映射到進(jìn)程地址空間后进统,進(jìn)程可以像訪問(wèn)普通內(nèi)存一樣對(duì)文件進(jìn)行訪問(wèn)。

通常一個(gè) App 需要加載 100 - 400 個(gè) dylibs浪听,但是其中的系統(tǒng)庫(kù)加載會(huì)被優(yōu)化螟碎,能在很快的時(shí)間內(nèi)加載完成

優(yōu)化 Load dylibs 過(guò)程

減少非系統(tǒng)庫(kù)的依賴合并非系統(tǒng)庫(kù)

Rebase / Binding

由于進(jìn)程是在自己私有的虛擬地址空間中啟動(dòng),按照傳統(tǒng)方式迹栓,該地址是固定可預(yù)見的掉分,這樣黑客只要找到一段進(jìn)程的地址,就很容易推算出整個(gè)程序的地址空間布局克伊。所以大部分操作系統(tǒng)都采用了地址空間布局隨機(jī)化 (ASLR) 的技術(shù)酥郭,避免攻擊防護(hù)。

Rebase 就是系統(tǒng)為了解決動(dòng)態(tài)虛擬地址沖突愿吹,在加載動(dòng)態(tài)庫(kù)時(shí)進(jìn)行的基地址重定位操作不从,Rebase 是如何工作呢?我們先看下內(nèi)存中的段 (segment) 是什么樣的犁跪。

內(nèi)存分為以下幾個(gè)段

__PAGEZERO: 32位系統(tǒng)中椿息,這是內(nèi)存中單獨(dú)的一個(gè)頁(yè)面 (4KB)。64位系統(tǒng)中坷衍,這個(gè)段對(duì)應(yīng)了一個(gè)完整的 32 位地址空間寝优,即前 4GB。這個(gè)段有助于捕捉空指針引用枫耳,或捕捉將整數(shù)當(dāng)做指針引用乏矾。

__TEXT: 存放程序代碼,只讀可執(zhí)行,由于該段數(shù)據(jù)是只讀的妻熊,同一個(gè)程序的多個(gè)實(shí)例可以僅使用一份 __TEXT 副本夸浅,從而可以優(yōu)化內(nèi)存

__LINKEDIT: 由 dyld 使用,包含了字符串扔役、符號(hào)表以及其他數(shù)據(jù)

__DATA: 可讀可寫的數(shù)據(jù)...

LINKEDIT 段中保存了 Rebase 的相關(guān)信息帆喇,dylib 中的 LC_DYLD_INOF_ONLY 指定 rebase info 在文件中的偏移量。

使用以下命令可以輸出 rebase 信息xcrun dyldinfo -rebase -bind -lazy_bind myapp.app/myapp
Mach-O 中包含了很多外部庫(kù)和符號(hào)的引用亿胸,使用動(dòng)態(tài)鏈接器加載外部庫(kù)和符號(hào)的過(guò)程叫做符號(hào)綁定 binding坯钦。

優(yōu)化 Rebase / Binding 過(guò)程

減少 __DATA 指針
減少 OC 的 metadata,可以刪除無(wú)用的 class/selector/category減少 C++ 虛函數(shù)使用 Swift 的 struct為不需要寫的屬性添加 readonly

ObjC

大部分 ObjC 的設(shè)置工作都在 Rebease 和 Binding 時(shí)做完類的定義被注冊(cè)實(shí)例變量偏移的更新Category 被插入到方法列表Selector 的唯一性

Initializers

C++ 為靜態(tài)變量初始化執(zhí)行 +load 方法執(zhí)行 main()

優(yōu)化 Initializers 過(guò)程

減少 +load() 方法侈玄,盡量使用 +initialize() 代替

使用 dispatch_one() pthread_once() std::once() 代替 C/C++ attribute(constructor)

減少靜態(tài)構(gòu)造函數(shù)

初始化方法中不要使用 dlopen()初始化方法中不要?jiǎng)?chuàng)建線程

總結(jié)優(yōu)化的點(diǎn)

減少非系統(tǒng)的framework依賴婉刀,如果framework 在當(dāng)前 App 支持的所有 iOS 系統(tǒng)版本中都存在則設(shè)為 required,否則設(shè)置為 optional序仙,optional 會(huì)有額外檢查合并非系統(tǒng)庫(kù)

刪除無(wú)用的 class/selector/category

刪除無(wú)用的方法調(diào)用突颊、靜態(tài)變量等減少 C++ 虛函數(shù)(減少創(chuàng)建虛函數(shù)表時(shí)間)

使用 Swift 的 struct (從而減少符號(hào)數(shù)量)

為不需要寫的屬性添加 readonly減少 +load() 方法,盡量使用 +initialize() 代替使用

dispatch_one() pthread_once() std::once() 代替 C/C++ attribute(constructor)
減少靜態(tài)構(gòu)造函數(shù)

初始化方法中不要使用 dlopen()

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末潘悼,一起剝皮案震驚了整個(gè)濱河市律秃,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌治唤,老刑警劉巖棒动,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異宾添,居然都是意外死亡船惨,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門缕陕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)粱锐,“玉大人,你說(shuō)我怎么就攤上這事榄檬〔贩叮” “怎么了?”我有些...
    開封第一講書人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵鹿榜,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我锦爵,道長(zhǎng)舱殿,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任险掀,我火速辦了婚禮沪袭,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘樟氢。我一直安慰自己冈绊,他們只是感情好侠鳄,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著死宣,像睡著了一般伟恶。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上毅该,一...
    開封第一講書人閱讀 49,036評(píng)論 1 285
  • 那天博秫,我揣著相機(jī)與錄音,去河邊找鬼眶掌。 笑死挡育,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的朴爬。 我是一名探鬼主播即寒,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼召噩!你這毒婦竟也來(lái)了母赵?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤蚣常,失蹤者是張志新(化名)和其女友劉穎市咽,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體抵蚊,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡施绎,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了贞绳。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片谷醉。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖冈闭,靈堂內(nèi)的尸體忽然破棺而出俱尼,到底是詐尸還是另有隱情,我是刑警寧澤萎攒,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布遇八,位于F島的核電站,受9級(jí)特大地震影響耍休,放射性物質(zhì)發(fā)生泄漏刃永。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一羊精、第九天 我趴在偏房一處隱蔽的房頂上張望斯够。 院中可真熱鬧,春花似錦、人聲如沸读规。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)束亏。三九已至铃在,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間枪汪,已是汗流浹背涌穆。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留雀久,地道東北人宿稀。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像赖捌,于是被迫代替她去往敵國(guó)和親祝沸。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容