我們?nèi)粘>幊讨心転閱?dòng)時(shí)間和包優(yōu)化做的

整個(gè)文章由來是一次內(nèi)部的分享酵熙, 是分享關(guān)于程序啟動(dòng)的一些事澄阳。從創(chuàng)建進(jìn)程到內(nèi)存分配斗遏,以及這些過程中的時(shí)間花費(fèi)脆炎。整個(gè)分享分為三部分梅猿,分別為:
1.理論部分:預(yù)備知識(shí)以及從點(diǎn)擊icon到程序啟動(dòng)完成都經(jīng)過了哪些過程。
2.實(shí)踐部分:具體看看程序啟動(dòng)有哪些流程和啟動(dòng)時(shí)間花在哪了秒裕。
3.具體建議:根據(jù)啟動(dòng)流程及時(shí)間瓶頸談?wù)剝?yōu)化建議袱蚓,建議更偏向每一個(gè)人能做的,如果是優(yōu)化工程團(tuán)隊(duì)的具體實(shí)踐請(qǐng)參考美團(tuán)關(guān)于APP冷啟動(dòng)時(shí)間優(yōu)化的實(shí)踐簇爆。
下面開始分享癞松。

首先是簡(jiǎn)單編譯原理介紹:


image.png

我們的 .h、.m 入蛆、亦或是.c响蓉、.mm、.swift 都會(huì)從高級(jí)語言被預(yù)處理哨毁、編譯枫甲、匯編、鏈接之后成為二進(jìn)制可執(zhí)行文件扼褪。

然后講一些預(yù)備知識(shí)想幻,如下:

  1. executable :就是我們 APP 的可執(zhí)行二進(jìn)制文件;
  2. dylib:動(dòng)態(tài)庫话浇,一般指程序啟動(dòng)時(shí)動(dòng)態(tài)鏈接的動(dòng)態(tài)庫脏毯;
  3. Bundle:動(dòng)態(tài)庫的一種,但是需要使用 dlopen() 打開幔崖,一般用于運(yùn)行時(shí)動(dòng)態(tài)打開食店;
  4. Framework:包含相關(guān)的資源和頭文件的動(dòng)態(tài)庫,如下圖中微信的 SDK赏寇;
image.png

我們來看看實(shí)際中我們的程序包是怎樣的:


image.png

將 .ipa 改成 .zip 然后打開吉嫩,由于這是直接編譯的 release 包,可以看到里面有符號(hào)表嗅定,一些extension 的支持以及支持 Swift 的動(dòng)態(tài)庫自娩,再往里就是 .app 文件以及打開后的二進(jìn)制文件、一堆資源和簽名文件等渠退。

關(guān)于我們的二進(jìn)制文件忙迁,可以用 MachOView.app 打開看到格式化后的一些信息脐彩,如下圖:


image.png

可以看到里面有加載這個(gè)二進(jìn)制文件到內(nèi)存中的一些信息,例如CPU類型动漾,文件的類型等丁屎。

然后簡(jiǎn)單介紹二進(jìn)制文件被加載到內(nèi)存中后是怎樣的映射關(guān)系:


image.png

引用 linux 內(nèi)存圖,可以看到:

  1. _PAGEZERO 填充在初始區(qū)域 (4G用捕獲空指針引用)旱眯;
  2. _Text 映射到代碼段晨川,_DATA 映射到數(shù)據(jù)段等;
  3. 引導(dǎo)開辟了堆删豺,共享庫映射區(qū)共虑,棧,dyld呀页,內(nèi)核代碼區(qū)等妈拌;

以上僅作為參考和教學(xué)用,實(shí)際情況相差較大且更為復(fù)雜蓬蝶。比如上圖沒有畫出因?yàn)?ASLR 隨機(jī)地址映射引起的段偏移尘分,并且實(shí)際情況下每個(gè)線程都是有自己的線程棧的,但是圖中都是表示為一塊區(qū)域丸氛。

下面我們進(jìn)入正題培愁,關(guān)于啟動(dòng)流程和啟動(dòng)時(shí)間我們需要關(guān)心的步驟:

兩部分:

  1. pre-main階段:
    1.1. 加載應(yīng)用的可執(zhí)行文件;
    1.2. 加載動(dòng)態(tài)鏈接庫加載器dyld(dynamic loader)缓窜;
    1.3. dyld遞歸加載應(yīng)用所有依賴的dylib(dynamic library 動(dòng)態(tài)鏈接庫)定续;

  2. main()階段:
    2.1. dyld調(diào)用main();
    2.2. 調(diào)用UIApplicationMain()禾锤;
    2.3. 調(diào)用applicationWillFinishLaunching私股;
    2.4. 調(diào)用didFinishLaunchingWithOptions;

表示為圖即是:


image.png

然后詳細(xì)介紹每個(gè)步驟:
1.execve(const char *filename, const char *argv[], const char *envp[]) : 一個(gè)內(nèi)核級(jí)系統(tǒng)調(diào)用函數(shù)恩掷,根據(jù)參數(shù)可知倡鲸,傳入一個(gè)文件名、一些命令行參數(shù)黄娘、一些環(huán)境變量然后開始打開這個(gè)文件(包含 fork 進(jìn)程并找到文件等過程)旦签,以下是細(xì)化的兩個(gè)步驟:
①. parse header:解析可執(zhí)行文件的 header 讀取文件的加載信息;
②. mmap 和 copyOnWrite:將可執(zhí)行文件映射進(jìn)內(nèi)存以及寫時(shí)復(fù)制技術(shù)(自行腦補(bǔ)懶加載)寸宏;

2.load dylib: 加載動(dòng)態(tài)庫的過程,大多為系統(tǒng)的動(dòng)態(tài)庫偿曙,下面細(xì)化:
①. 依據(jù)Apple的動(dòng)態(tài)鏈接器 dylib 進(jìn)行遞歸加載動(dòng)態(tài)庫及其動(dòng)態(tài)庫依賴的動(dòng)態(tài)庫氮凝;
②. rebase (ASLR矾缓,page fault 和 COW) 交惯,由于 ASLR 隨機(jī)地址偏移的存在开呐,所以需要修正我們指針的引用序臂,這一步主要是讀取并修正我們二進(jìn)制文件中的指針引用,主要是IO操作 稿壁;
③. bind (lazy bind) 幽钢,由于動(dòng)態(tài)庫共享內(nèi)存的使用,我們需要鏈接動(dòng)態(tài)庫后才知道我們二進(jìn)制文件中指向動(dòng)態(tài)庫內(nèi)容的指針實(shí)際的地址值傅是,所以需要鏈接時(shí)修正這些值匪燕,大部分是 (lazy bind) 即第一次訪問這個(gè)指針才去尋找這個(gè)指針實(shí)際指向的地址值并綁定供后續(xù)使用,所以首次訪問會(huì)慢一些喧笔,這一步主要是 CPU 尋找和計(jì)算指針值帽驯;

3.objc runtime(objc setup):這一步主要是OC運(yùn)行時(shí)的一些初始化工程,以下細(xì)分:
①. register class (class name map to class):類注冊(cè)书闸,動(dòng)態(tài)語言運(yùn)行時(shí)的需要尼变,會(huì)將類注冊(cè)到全局注冊(cè)表中,這個(gè)注冊(cè)表是一個(gè)字典浆劲,key 是類名的字符串嫌术,類對(duì)象本身是值;
②. 讀取 protocol牌借、category :讀取協(xié)議以及分類度气,并將分類方法加入對(duì)應(yīng)類的方法列表并保證其唯一性;

4.Initializers:這一步主要是+ load 這個(gè)方法以及一些全局變量的初始化
①. + load

添加標(biāo)志就能在啟動(dòng)時(shí)在控制臺(tái)打印啟動(dòng)時(shí)間:


image.png

我們來看看APP的啟動(dòng)時(shí)間:


image.png

可以看到四個(gè)過程對(duì)應(yīng)本文之前說的四個(gè)過程走哺。

最后針對(duì)四個(gè)過程談?wù)勎覀兤綍r(shí)編程中有什么事可以做的更好的:

1.Dylib Loading:
①.減少或者合并動(dòng)態(tài)庫蚯嫌;
②.這里和我們相關(guān)無非是引入第三方時(shí)斟酌這個(gè)第三方導(dǎo)致引入動(dòng)態(tài)庫相關(guān)的問題,這里和平時(shí)開發(fā)相關(guān)性較小就跳過了丙躏;

2.Objc setup:
相關(guān)過程:
①. class registration择示;
②.Non-fragile ivars offsets updated;
③.category registration晒旅;
④.selector uniquing 栅盲。

我們能做的:
①. 不要濫用分類,繼承等特性废恋,合并小類;
②. 不宜過長的類和方法名
③. 重構(gòu)谈秫、改版的時(shí)候及時(shí)刪除不再使用但是還有引用的老類和方法等,不要害怕以后會(huì)用到就用注釋掉的方式鱼鼓,害怕丟失版本管理也可以找到拟烫;
④. 屬性可以標(biāo)記為 readonly 就不要標(biāo)記 readwrite 可以少生成方法;

3.rebase/binding:
這里主要就是修正指針的消耗迄本,例如類硕淑、分類和成員變量和方法,這里能做的在之前講 Objc setup 的時(shí)候已經(jīng)說過這里就不再贅述。

參考清理無用方法:
http://stackoverflow.com/questions/35233564/how-to-find-unused-code-in-xcode-7
https://developer.Apple.com/library/ios/documentation/ToolsLanguages/Conceptual/Xcode_Overview/CheckingCodeCoverage.html

參考清理無用類的一些清理工具:
AppCode
FUI

4.Initializers:
這一步和我們相關(guān)主要是 + load 這個(gè)方法以及一些全局變量的初始化置媳。
①.不要在初始化時(shí)做耗時(shí)工作于樟;
②.盡量不要使用 + load 方法,我們工程中大量在 + load 中加邏輯拇囊,很多其實(shí)是沒有必要的迂曲;
③.使用 + initialize 或者別的地方并配合 dispatch_once 是更好的選擇;
④. 如果是為了執(zhí)行一次,在 + load 中是不能保證的寥袭,因?yàn)檫@個(gè)方法是可以被調(diào)用的路捧,比如 [People load] ;
⑤.即使寫在 + load 里配合 dispatch_once 也是更安全的選擇纠永;
⑥.使用 swift 鬓长, swift 實(shí)現(xiàn)靜態(tài)變量初始化時(shí)是使用類似 dispatch_once 這樣的技術(shù)在首次調(diào)用時(shí)初始化的;

最后(重點(diǎn)):
重點(diǎn)談?wù)?Swift 對(duì)我們的幫助:

  1. 值類型(value type) 例如 struct尝江、enum 是在棧上分配的涉波,我們知道棧上是運(yùn)行時(shí)分配,并且值類型是不需要像類一樣全局注冊(cè)的炭序,也沒有那么多指針啤覆。這里重點(diǎn)談?wù)剹5暮锰帲?br> ①. 效率更高,因?yàn)椴恍枰穸岩粯泳S護(hù)全局引用計(jì)數(shù)惭聂,計(jì)算計(jì)數(shù)完成還要去尋找空內(nèi)存窗声,分配完畢之后還需要釋放,釋放后還要合并小內(nèi)存等一系列需要加鎖保證同步的繁瑣工作辜纲;每個(gè)線程有自己的線程棧笨觅,所以是不需要同步的,也沒有那么多維護(hù)加鎖引用計(jì)數(shù)的開銷耕腾,所以棧上分配是你更好的選擇见剩;
    ②. 面向協(xié)議變成是完全可以替代面向?qū)ο螅悾┚幊痰模⑶夷氵€有 Swift 強(qiáng)大的泛型系統(tǒng)支持扫俺;
    ③. OC 的 Category 開銷是很大的苍苞,因?yàn)槭擎溄与A段進(jìn)行的并且會(huì)引入大量的指針,而 Swift 的 extension 就沒有這方面問題狼纬,因?yàn)樗詈髸?huì)和它相關(guān)的 class 或 struct羹呵、enum 一起編譯。
    ④. 更強(qiáng)大的 feature 讓你寫出更少疗琉,更優(yōu)雅的代碼冈欢;
    ⑤. 強(qiáng)大的泛型系統(tǒng),和泛型特化等優(yōu)化會(huì)讓你的很多代碼可以被內(nèi)聯(lián)優(yōu)化盈简,這樣可以減少大量指針凑耻;
    ⑥. 你可以使用 final犯戏、private 等修飾符讓IDE去保證而不是依靠文檔,并且這些優(yōu)化關(guān)鍵字配合 Swift whole module 優(yōu)化能力可以讓你的代碼將方法動(dòng)態(tài)派發(fā)轉(zhuǎn)化為靜態(tài)派發(fā)拳话,不僅提升了效率還減少的指針。
    ⑦. Swift 中值類型以及 class 可以沒有父類的特性讓你可以避免 OC 龐大而復(fù)雜的繼承體系种吸,這樣也會(huì)讓APP跑的更快弃衍,內(nèi)存使用更小。
    ⑧. 命名空間和更嚴(yán)格的作用域也讓編程更加愉快坚俗。

希望這些建議能對(duì)編寫代碼有更高追求的你以幫助镜盯,以上。

引用:
WWDC 2016:Optimizing App Startup Time
WWDC 2015: Optimizing Swift Performance
WWDC 2016: Understanding Swift Performance

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末猖败,一起剝皮案震驚了整個(gè)濱河市速缆,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌恩闻,老刑警劉巖艺糜,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異幢尚,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)真慢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門朗鸠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事弦赖。” “怎么了列另?”我有些...
    開封第一講書人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵店乐,是天一觀的道長左电。 經(jīng)常有香客問我,道長枕扫,這世上最難降的妖魔是什么染簇? 我笑而不...
    開封第一講書人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任青灼,我火速辦了婚禮暴心,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘杂拨。我一直安慰自己专普,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開白布弹沽。 她就那樣靜靜地躺著檀夹,像睡著了一般筋粗。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上炸渡,一...
    開封第一講書人閱讀 49,166評(píng)論 1 284
  • 那天娜亿,我揣著相機(jī)與錄音,去河邊找鬼蚌堵。 笑死暇唾,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的辰斋。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼瘸味,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼宫仗!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起旁仿,我...
    開封第一講書人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤藕夫,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后枯冈,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體毅贮,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年尘奏,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了滩褥。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡炫加,死狀恐怖瑰煎,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情俗孝,我是刑警寧澤酒甸,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站赋铝,受9級(jí)特大地震影響插勤,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜革骨,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一农尖、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧苛蒲,春花似錦卤橄、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽喇颁。三九已至,卻和暖如春嚎货,著一層夾襖步出監(jiān)牢的瞬間橘霎,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來泰國打工殖属, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留姐叁,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓洗显,卻偏偏與公主長得像外潜,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子挠唆,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

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

  • 1处窥、通過CocoaPods安裝項(xiàng)目名稱項(xiàng)目信息 AFNetworking網(wǎng)絡(luò)請(qǐng)求組件 FMDB本地?cái)?shù)據(jù)庫組件 SD...
    陽明先生_x閱讀 15,968評(píng)論 3 119
  • 耳機(jī) 手機(jī) 灰色 病了 我 無藥可救了
    窮奇閱讀 287評(píng)論 0 1
  • 今天完成小鞋子的鞋幫編織,明天開始鞋面的編織玄组。對(duì)于第一次編制的我來說滔驾,這個(gè)過程好艱辛。等熟練掌握了俄讹,也行一天就能搞定吧哆致!
    甜趣陶心閱讀 133評(píng)論 0 0
  • 大連踪蹬,青島驹溃,這樣的城市姓名,被人們冠以美好的名詞延曙。它們都與海有些密切的關(guān)聯(lián)豌鹤,陽光,海浪枝缔,沙灘布疙!多么愜意,多么休閑愿卸,...
    六葉冰晶閱讀 393評(píng)論 0 0
  • 周末灵临,去逛街,喜歡上了一件白色旗袍趴荸。價(jià)錢是幾百塊儒溉,我覺得有點(diǎn)貴,當(dāng)然這個(gè)價(jià)錢只是對(duì)我們這種不能掙啥錢发钝,家里也沒啥錢...
    墻角薔薇閱讀 548評(píng)論 0 0