iOS應(yīng)用啟動時間統(tǒng)計和優(yōu)化方案總結(jié)

冷啟動和熱啟動

1.冷啟動:APP 的第一次打開,或者被kill掉之后重新打開的啟動過程為冷啟動牧愁。
2.熱啟動:就是按下home鍵的時候了嚎,app還存在一段時間,這時點擊app馬上就能恢復(fù)到原狀態(tài)咧七,這種啟動我們稱為熱啟動衰齐。
應(yīng)用啟動時間,直接影響用戶對一款應(yīng)用的判斷和使用體驗继阻,冷啟動的時間的是我們關(guān)注比較多的方向耻涛,也是需要注重優(yōu)化的地方,解析會詳細(xì)介紹app冷啟動的過程和優(yōu)化策略瘟檩。

冷啟動的耗時計算

t(App總啟動時間) = t1(main()之前的加載時間) + t2(main()之后的加載時間)抹缕。

main()之前的加載過程

dyld

大致的過程如下:

  1. 加載dyld到App進(jìn)程
  2. 加載動態(tài)庫(包括所依賴的所有動態(tài)庫)
  3. Rebase image
  4. Bind image
  5. Objc setup
  6. 初始化

加載動態(tài)庫

App開始啟動后,系統(tǒng)首先加載可執(zhí)行文件(自身App的所有.o文件的集合)墨辛,然后使用dyld加載動態(tài)鏈接庫卓研。dyld是一個專門用來加載動態(tài)鏈接庫的庫。

dyld的全稱是dynamic loader,它的作用是加載一個進(jìn)程所需要的image(可執(zhí)行文件奏赘、動態(tài)鏈接庫等)寥闪,dyld是開源的

執(zhí)行從dyld開始磨淌,dyld從可執(zhí)行文件的依賴開始, 遞歸加載所有的依賴動態(tài)鏈接庫疲憋。在每個動態(tài)庫的加載過程中, dyld需要:

  1. 分析所依賴的動態(tài)庫
  2. 找到動態(tài)庫的mach-o文件
  3. 打開文件
  4. 驗證文件
  5. 在系統(tǒng)核心注冊文件簽名
  6. 對動態(tài)庫的每一個segment調(diào)用mmap()

動態(tài)鏈接庫包括:iOS 中用到的所有系統(tǒng) framework梁只,加載OC runtime方法的libobjc缚柳,系統(tǒng)級別的libSystem,例如libdispatch(GCD)和libsystem_blocks (Block)搪锣。動態(tài)鏈接庫有以下好處:代碼共用秋忙,很多程序都動態(tài)鏈接了這些 lib,但它們在內(nèi)存和磁盤中只有一份构舟; 易于維護(hù)灰追,由于被依賴的 lib 是程序執(zhí)行時才鏈接的,所以這些 lib 很容易做更新旁壮。

通常的监嗜,一個App需要加載100到400個dylibs, 但是其中的系統(tǒng)庫被優(yōu)化抡谐,可以很快的加載裁奇。 針對這一步驟的優(yōu)化有:

  1. 減少非系統(tǒng)庫的依賴
  2. 合并非系統(tǒng)庫
  3. 使用靜態(tài)資源,比如把代碼加入主程序

Rebase && Bind

ASLR(Address space layout randomization)地址空間布局隨機化麦撵,是一種針對緩沖區(qū)溢出的安全保護(hù)技術(shù)刽肠,通過對堆、棧免胃、共享庫映射等線性區(qū)布局的隨機化音五,增加攻擊者預(yù)測目的地址的難度,防止攻擊者直接定位攻擊代碼位置羔沙,達(dá)到阻止溢出攻擊的目的躺涝。大部分主流的操作系統(tǒng)已經(jīng)實現(xiàn)了ASLR,Apple在iOS4.3開始導(dǎo)入了ASLR扼雏。

由于ASLR的存在坚嗜,可執(zhí)行文件和動態(tài)鏈接庫在虛擬內(nèi)存中的加載地址每次啟動都不固定,所以需要這2步來修復(fù)鏡像中的資源指針诗充,來指向正確的地址苍蔬。 Rebase修復(fù)的是指向當(dāng)前鏡像內(nèi)部的資源指針; 而Bind指向的是鏡像外部的資源指針蝴蜓。

  • Rebase步驟先進(jìn)行碟绑,需要把鏡像讀入內(nèi)存俺猿,并以page為單位進(jìn)行加密驗證,保證不會被篡改格仲,所以這一步的瓶頸在IO押袍。
  • Bind在其后進(jìn)行,由于要查詢符號表抓狭,來指向跨鏡像的資源伯病,加上在rebase階段,鏡像已被讀入和加密驗證否过,所以這一步的瓶頸在于CPU計算。

優(yōu)化該階段的關(guān)鍵在于減少__DATA segment中的指針數(shù)量惭蟋。我們可以優(yōu)化的點有:

  • 減少Objc類數(shù)量苗桂, 減少selector數(shù)量
  • 減少C++虛函數(shù)數(shù)量
  • 轉(zhuǎn)而使用swift struct(其實本質(zhì)上就是為了減少符號的數(shù)量)
rebase&&bind.png

Objc setup

這一步主要工作是:

  • 注冊O(shè)bjc類 (class registration)
  • 把category的定義插入方法列表 (category registration)
  • 保證每一個selector唯一 (selctor uniquing)

由于之前2步驟的優(yōu)化,這一步實際上沒有什么可做的告组。

Initializers

以上三步屬于靜態(tài)調(diào)整(fix-up)煤伟,都是在修改__DATA segment中的內(nèi)容,而這里則開始動態(tài)調(diào)整木缝,開始在堆和堆棧中寫入內(nèi)容便锨。 在這里的工作有:

  1. Objc的+load()函數(shù)
  2. C++的構(gòu)造函數(shù)屬性函數(shù) 形如attribute((constructor)) void DoSomeInitializationWork()
  3. 非基本類型的C++靜態(tài)全局變量的創(chuàng)建(通常是類或結(jié)構(gòu)體)(non-trivial initializer) 比如一個全局靜態(tài)結(jié)構(gòu)體的構(gòu)建,如果在構(gòu)造函數(shù)中有繁重的工作我碟,那么會拖慢啟動速度放案。

至此,可執(zhí)行文件中和動態(tài)庫所有的符號(Class矫俺,Protocol吱殉,Selector,IMP厘托,…)都已經(jīng)按格式成功加載到內(nèi)存中友雳,被 runtime 所管理,再這之后铅匹,runtime 的那些方法(動態(tài)添加 Class押赊、swizzle 等等)才能生效。

main()之前的加載時間

在不越獄的情況下包斑,以往很難精確的測量在main()函數(shù)之前的啟動耗時流礁,因而我們也往往容易忽略掉這部分?jǐn)?shù)據(jù)。小型App確實不需要太過關(guān)注這部分舰始。但如果是大型App(自定義的動態(tài)庫超過50個崇棠、或編譯結(jié)果二進(jìn)制文件超過30MB),這部分耗時將會變得突出丸卷。所幸枕稀,蘋果已經(jīng)在Xcode中加入這部分的支持。

在Xcode的菜單中選擇ProjectSchemeEdit Scheme,然后找到 RunEnvironment Variables+萎坷,添加name為DYLD_PRINT_STATISTICS凹联,value為1的環(huán)境變量。

DYLD_PRINT_STATISTICS
Total pre-main time:  43.00 milliseconds (100.0%)
         dylib loading time:  19.01 milliseconds (44.2%)
        rebase/binding time:   1.77 milliseconds (4.1%)
            ObjC setup time:   3.98 milliseconds (9.2%)
           initializer time:  18.17 milliseconds (42.2%)
           slowest intializers :
             libSystem.B.dylib :   2.56 milliseconds (5.9%)
   libBacktraceRecording.dylib :   3.00 milliseconds (6.9%)
    libMainThreadChecker.dylib :   8.26 milliseconds (19.2%)
                       ModelIO :   1.37 milliseconds (3.1%)

main()前的優(yōu)化總結(jié):

  1. 減少不必要的framework哆档,因為動態(tài)鏈接比較耗時
  2. check framework應(yīng)當(dāng)設(shè)為optional和required蔽挠,如果該framework在當(dāng)前App支持的所有iOS系統(tǒng)版本都存在,那么就設(shè)為required瓜浸,否則就設(shè)為optional澳淑,因為optional會有些額外的檢查
  3. 合并或者刪減一些OC類,關(guān)于清理項目中沒用到的類插佛,使用工具AppCode代碼檢查功能杠巡,查到當(dāng)前項目中沒有用到的類、方法雇寇、變量等:
     1. 刪減一些無用的靜態(tài)變量
     2. 刪減沒有被調(diào)用到或者已經(jīng)廢棄的方法氢拥,方法見:StackOverflowApple
  4. 將不必須在+load方法中做的事情延遲到+initialize中
  5. 盡量不要用C++虛函數(shù)(創(chuàng)建虛函數(shù)表有開銷)

main()調(diào)用之后優(yōu)化

從main()函數(shù)開始至applicationWillFinishLaunching結(jié)束锨侯,我們統(tǒng)一稱為main()函數(shù)之后的部分嫩海,在main()被調(diào)用之后,App的主要工作就是初始化必要的服務(wù)囚痴,顯示首頁內(nèi)容等叁怪。而我們的優(yōu)化也是圍繞如何能夠快速展現(xiàn)首頁來開展。 main()函數(shù)之后耗時的影響因素:

  1. 執(zhí)行main()函數(shù)的耗時
  2. 執(zhí)行applicationWillFinishLaunching的耗時
  3. rootViewController及其childViewController的加載渡讼、view及其subviews的加載

這個過程的時間統(tǒng)計可以在起止位置埋點獲取時間戳骂束,開始位置時main()開始執(zhí)行的位置,結(jié)束位置是applicationWillFinishLaunching開始執(zhí)行的位置成箫,將兩個時間戳相減可以獲取main()調(diào)用之后的耗時展箱。對于main()函數(shù)調(diào)用之后我們可以優(yōu)化的點有:

  1. 不使用xib,直接使用代碼加載首頁視圖蹬昌;
  2. NSUserDefaults實際上是在Library文件夾下會生產(chǎn)一個plist文件混驰,如果文件太大的話一次能讀取到內(nèi)存中可能很耗時,這個影響需要評估皂贩,如果耗時很大的話需要拆分(需考慮老版本覆蓋安裝兼容問題)栖榨;
  3. 每次用NSLog方式打印會隱式的創(chuàng)建一個Calendar,因此需要刪減啟動時各業(yè)務(wù)方打的log明刷,或者僅僅針對內(nèi)測版輸出log婴栽;
  4. 梳理應(yīng)用啟動時發(fā)送的所有網(wǎng)絡(luò)請求,是否可以統(tǒng)一在異步線程請求辈末;

以上就是iOS應(yīng)用啟動時間統(tǒng)計和優(yōu)化方案總結(jié)

參考文獻(xiàn):

  1. 深入理解iOS App的啟動過程
  2. iOS App 啟動性能優(yōu)化
  3. 今日頭條iOS客戶端啟動速度優(yōu)化
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末愚争,一起剝皮案震驚了整個濱河市映皆,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌轰枝,老刑警劉巖捅彻,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異鞍陨,居然都是意外死亡步淹,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進(jìn)店門诚撵,熙熙樓的掌柜王于貴愁眉苦臉地迎上來缭裆,“玉大人,你說我怎么就攤上這事砾脑∮资唬” “怎么了?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵韧衣,是天一觀的道長。 經(jīng)常有香客問我购桑,道長畅铭,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任勃蜘,我火速辦了婚禮硕噩,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘缭贡。我一直安慰自己炉擅,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布阳惹。 她就那樣靜靜地躺著谍失,像睡著了一般。 火紅的嫁衣襯著肌膚如雪莹汤。 梳的紋絲不亂的頭發(fā)上快鱼,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天,我揣著相機與錄音纲岭,去河邊找鬼抹竹。 笑死,一個胖子當(dāng)著我的面吹牛止潮,可吹牛的內(nèi)容都是我干的窃判。 我是一名探鬼主播,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼喇闸,長吁一口氣:“原來是場噩夢啊……” “哼袄琳!你這毒婦竟也來了询件?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤跨蟹,失蹤者是張志新(化名)和其女友劉穎雳殊,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體窗轩,經(jīng)...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡夯秃,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了痢艺。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片仓洼。...
    茶點故事閱讀 39,779評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖堤舒,靈堂內(nèi)的尸體忽然破棺而出色建,到底是詐尸還是另有隱情,我是刑警寧澤舌缤,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布箕戳,位于F島的核電站,受9級特大地震影響国撵,放射性物質(zhì)發(fā)生泄漏陵吸。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一介牙、第九天 我趴在偏房一處隱蔽的房頂上張望壮虫。 院中可真熱鬧,春花似錦环础、人聲如沸囚似。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽饶唤。三九已至,卻和暖如春框都,著一層夾襖步出監(jiān)牢的瞬間搬素,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工魏保, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留熬尺,地道東北人。 一個月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓谓罗,卻偏偏與公主長得像粱哼,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子檩咱,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,700評論 2 354