iOS App 啟動優(yōu)化
前言
作為程序猿來說,“性能優(yōu)化”是我們都很熟悉的詞,也是我們需要不斷努以及持續(xù)進的事情;其實優(yōu)化是個很嚴(yán)謹(jǐn)?shù)恼n題,因為細(xì)分來說的話有種優(yōu)化向 ,但是切忌在實際開發(fā)過程中不能盲目的為了優(yōu)化而優(yōu)化,這樣有時可能會造成適得其反的負(fù)效果,需要我們根據(jù)實際場景以及業(yè)務(wù)需求進合理優(yōu)化。接下來進入正題,本文將會以iOS App的啟動優(yōu)化為展開點進探討。
啟動流程:
iOS App 的啟動我們都知道分為pre-main 和 main() 兩個階段,并且在這兩個階段中,系統(tǒng)會進行系列的加載操作,過程如下:
1、pre-main階段
1. 加載應(yīng)的可執(zhí)件
2. 加載dyld動態(tài)連接器
3. dyld遞歸加載應(yīng)所有依賴的動態(tài)鏈接庫dylib
2、main()階段
1. dyld調(diào) main()
2. 調(diào)UIApplicationMain()
3. 調(diào)applicationWillFinishLaunching
4. 調(diào)didFinishLaunchingWithOptions
階段優(yōu)化項
1、pre-main階段
針對 pre-main 階段做優(yōu)化時,我們需要先詳細(xì)了解其加載過程,這個可以在2016年WWDC 的 Optimizing App Startup Time 中詳細(xì)了解到, 相關(guān)材料
1.1 Load dylibs
這階段dyld分析應(yīng)依賴的 dylib (xcode7以后.dylib已改為名.tbd),找到其 mach-o 件,打開和讀取這些件并驗證其有效性,接著會找到代碼簽名注冊到內(nèi)核,最后對 dylib 的每個 segment 調(diào) mmap()。不過這的 dylib 部分都是系統(tǒng)庫,不需要我們?nèi)プ鲱~外的優(yōu)化。
優(yōu)化結(jié)論
1.2 Rebase/Bind
在dylib的加載過程中,系統(tǒng)為了安全考慮,引了ASLR (Address Space Layout Randomization)技術(shù)和 代碼簽名。由于ASLR的存在,鏡像(Image,包括可執(zhí)件、 dylib和bundle)會在隨機的地址上加載,和 之前指針指向的地址(preferred_address)會有個偏差(slide), dyld需要修正這個偏差,來指向正確的 地址。 Rebase在前, Bind在后, Rebase做的是將鏡像讀內(nèi)存,修正鏡像內(nèi)部的指針,性能消耗主要在 IO。 Bind做的是查詢符號表,設(shè)置指向鏡像外部的指針,性能消耗主要在CPU計算。
優(yōu)化結(jié)論:
1.3 Objc setup
部分ObjC初始化作已經(jīng)在Rebase/Bind階段做完了,這步dyld會注冊所有聲明過的ObjC類,將分類插 到類的法列表,再檢查每個selector的唯性。
在這步倒沒什么優(yōu)化可做的, Rebase/Bind階段優(yōu)化好了,這步的耗時也會減少。
1.4 Initializers
在這階段, dyld開始運程序的初始化函數(shù),調(diào)每個Objc類和分類的+load法,調(diào)C/C++ 中的構(gòu)造器 函數(shù)(attribute((constructor))修飾的函數(shù)),和創(chuàng)建基本類型的C++靜態(tài)全局變量。 Initializers階段執(zhí) 完后, dyld開始調(diào)main()函數(shù)。
優(yōu)化結(jié)論:
2、main()階段
在這階段,主要優(yōu)化重點放在 SDK初始化、業(yè)務(wù)具注冊、整體
didFinishLaunchingWithOptions 法中,因為我們的些第三 app 格配置、啟動引導(dǎo)顯示狀態(tài)邏輯、版本更新邏輯等等基本都會在這進,如果這部分邏輯沒有做好優(yōu)化梳理,隨著業(yè)務(wù)不斷拓展,臃腫的業(yè)務(wù)邏輯會直接導(dǎo)致啟動時 間加。
場景補充:
另外,在我們實際開發(fā)過程中,很多項的控制器都會有些后臺可配、較為豐富的結(jié)構(gòu)或者推薦數(shù)據(jù) 進展示,且我們的展示速度通常也會被納啟動優(yōu)化的部分,其實對于這種類型的優(yōu)化,如果我 們還只是傳統(tǒng)的 api -> data -> UI 式進的話,就很難有明顯的改善空間,因為戶的絡(luò)狀態(tài) 并不是可控項,如果不做其他處理的話,那在很多場景下對戶來說,即使我們放上些占位圖,展示的樣式也是很不友好的,畢竟控制器對戶的第視覺沖擊影響還是較的。
對于這種場景下的優(yōu)化來說,般我們可以采取 Local + Network + Update 的式在定程度上優(yōu)化 加載速度: 即:
這樣做的好處是
當(dāng)然這種也并不是唯的應(yīng)對式,且也并對所有場景都適,只是提供種思路已,還是需要根據(jù) 項的實際場景選擇適合的優(yōu)化案。
統(tǒng)計時
另外如果在開發(fā)過程中,我們想直觀的查看 app 啟動期間,各階段的耗時情況,也可以在Xcode,的 edit scheme 設(shè)置添加 DYLD_PRINT_STATISTICS 為1 ,打印啟動時,例如
優(yōu)化前啟動時:
優(yōu)化后啟動時:
當(dāng)然,這些log我們僅僅只能在開發(fā)調(diào)試階段查看打印,那么在實際項中,我們需要對線上項的啟動數(shù)據(jù) 進監(jiān)控,以便及時的定位和優(yōu)化那些影響 app 啟動時的環(huán)節(jié),這時我們應(yīng)該怎樣更好的處理呢?
當(dāng)然我們可以通過服務(wù)器埋點上報的式統(tǒng)計分析,不過這樣來會發(fā)現(xiàn)我們的統(tǒng)計成本就會增 加,且結(jié)果分析也會變得不那么靈活。所以這推薦種簡單的監(jiān)控式,那就是友盟的 U-APM 應(yīng)能性 能監(jiān)控SDK ,只需要我們進簡單的pod集成之后,便可根據(jù)我們的實際需要進動或者動監(jiān)控啟動數(shù) 據(jù),詳情可以參考 U-APM, 并且為了便我們對數(shù)據(jù)進分析,友盟后臺已經(jīng)根據(jù)這些數(shù)據(jù)幫我們繪制出 了對應(yīng)的分布圖,我們可以了然的得出啟動耗時分布、啟動類型占等等,如圖:
除此之外,我們還可以通過SDK進崩潰分析、 ANR分析、監(jiān)控告警、卡頓分析、內(nèi)存分析等等諸多功能, 有了 U-APM 這個監(jiān)控平臺,其實在實際開發(fā)過程中很程度的提升了我們對線上 app 的優(yōu)化分析效率。
當(dāng)然本的介紹也只是較淺顯的優(yōu)化項,僅供參考以及思路引導(dǎo),優(yōu)化之路任重道遠(yuǎn),還需要我們不斷 的去探索、發(fā)現(xiàn)、提。不過最后還是要提醒句:在實際項開發(fā)過程中,不要為了優(yōu)化優(yōu)化,要根據(jù) 項情況有針對性的進優(yōu)化。