iOS App 启动优化
簡(jiǎn)介:?作為程序猿來(lái)說(shuō),“性能優(yōu)化”是我們都很熟悉的詞,也是我們需要不斷努?以及持續(xù)進(jìn)?的事情;其實(shí)優(yōu)化是?個(gè)很?的課題,因?yàn)榧?xì)分來(lái)說(shuō)的話有??????種優(yōu)化?向 ,但是切忌在實(shí)際開發(fā)過(guò)程中不能盲?的 為了優(yōu)化?優(yōu)化,這樣有時(shí)可能會(huì)造成適得其反的負(fù)效果,需要我們根據(jù)實(shí)際場(chǎng)景以及業(yè)務(wù)需求進(jìn)?合理優(yōu) 化。接下來(lái)進(jìn)?正題,本?將會(huì)以iOS App的啟動(dòng)優(yōu)化為展開點(diǎn)進(jìn)?探討。
前言
作為程序猿來(lái)說(shuō),“性能優(yōu)化”是我們都很熟悉的詞,也是我們需要不斷努?以及持續(xù)進(jìn)?的事情;其實(shí)優(yōu)化是?個(gè)很?的課題,因?yàn)榧?xì)分來(lái)說(shuō)的話有??????種優(yōu)化?向 ,但是切忌在實(shí)際開發(fā)過(guò)程中不能盲?的 為了優(yōu)化?優(yōu)化,這樣有時(shí)可能會(huì)造成適得其反的負(fù)效果,需要我們根據(jù)實(shí)際場(chǎng)景以及業(yè)務(wù)需求進(jìn)?合理優(yōu)?化。接下來(lái)進(jìn)?正題,本?將會(huì)以iOS App的啟動(dòng)優(yōu)化為展開點(diǎn)進(jìn)?探討。
啟動(dòng)流程:
iOS App 的啟動(dòng)我們都知道分為 ?為pre-main 和 ?main() 兩個(gè)階段,并且在這兩個(gè)階段中,系統(tǒng)會(huì)進(jìn) ??系列的加載操作,過(guò)程如下:
1、pre-main階段
1. ?加載應(yīng)?的可執(zhí)??件
2. ?加載dyld動(dòng)態(tài)連接器
3. ?dyld遞歸加載應(yīng)?所有依賴的動(dòng)態(tài)鏈接庫(kù)dylib
2、main()階段
1. ?dyld調(diào)? ?main()
2. ?調(diào)?UIApplicationMain()
3. ?調(diào)?applicationWillFinishLaunching
4. ?調(diào)?didFinishLaunchingWithOptions
階段優(yōu)化項(xiàng)
1、pre-main階段
針對(duì) ?pre-main 階段做優(yōu)化時(shí),我們需要先詳細(xì)了解其加載過(guò)程,這個(gè)可以在2016年WWDC 的 ?Optimizing App Startup Time 中詳細(xì)了解到, ? 相關(guān)材料
1.1 Load dylibs
這?階段dyld會(huì)分析應(yīng)?依賴的 ?dylib (xcode7以后.dylib已改為名.tbd),找到其 ?mach-o ?件,打開和讀取這些?件并驗(yàn)證其有效性,接著會(huì)找到代碼簽名注冊(cè)到內(nèi)核,最后對(duì) ?dylib 的每?個(gè) ?segment 調(diào)? mmap()。不過(guò)這?的 ?dylib ?部分都是系統(tǒng)庫(kù),不需要我們?nèi)プ鲱~外的優(yōu)化。
優(yōu)化結(jié)論:
| 1、盡量不使?內(nèi)嵌的dylib,從?避免增加?`Load?dylibs`開銷 2、合并已有的dylib和使?靜態(tài)庫(kù)(static?archives),減少dylib的使?個(gè)數(shù) 3、懶加載dylib,但是要注意dlopen()可能造成?些問(wèn)題,且實(shí)際上懶加載做的?作會(huì)更多 |
1.2 Rebase/Bind
在dylib的加載過(guò)程中,系統(tǒng)為了安全考慮,引?了ASLR (Address Space Layout Randomization)技術(shù)和 ? ?代碼簽名。由于ASLR的存在,鏡像(Image,包括可執(zhí)??件、 ?dylib和bundle)會(huì)在隨機(jī)的地址上加載,和 之前指針指向的地址(preferred_address)會(huì)有?個(gè)偏差(slide), dyld需要修正這個(gè)偏差,來(lái)指向正確的?地址。 ? Rebase在前, ? Bind在后, ?Rebase做的是將鏡像讀?內(nèi)存,修正鏡像內(nèi)部的指針,性能消耗主要在 ? ? IO。 Bind做的是查詢符號(hào)表,設(shè)置指向鏡像外部的指針,性能消耗主要在CPU計(jì)算。
優(yōu)化結(jié)論:
| 在此過(guò)程中,我們需要注意的是盡量減少指針數(shù)量,?如: 1.?減少ObjC類(class)、?法(selector)、分類(category)的數(shù)量 2.?減少C++虛函數(shù)的的數(shù)量(創(chuàng)建虛函數(shù)表有開銷) 3.?使??Swift?struct?(內(nèi)部做了優(yōu)化,符號(hào)數(shù)量更少) |
1.3 Objc setup
?部分ObjC初始化?作已經(jīng)在Rebase/Bind階段做完了,這?步dyld會(huì)注冊(cè)所有聲明過(guò)的ObjC類,將分類插 ?到類的?法列表?,再檢查每個(gè)selector的唯?性。
在這?步倒沒什么優(yōu)化可做的, ?Rebase/Bind階段優(yōu)化好了,這?步的耗時(shí)也會(huì)減少。
1.4 Initializers
在這?階段, ? dyld開始運(yùn)?程序的初始化函數(shù),調(diào)?每個(gè)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é)論:
| 1.?少在類的+load?法?做事情,盡量把這些事情推遲到+initiailize 2.?減少構(gòu)造器函數(shù)個(gè)數(shù),在構(gòu)造器函數(shù)?少做些事情 3.?減少構(gòu)造器函數(shù)個(gè)數(shù),在構(gòu)造器函數(shù)?少做些事情 |
2、main()階段
在這?階段?,主要優(yōu)化重點(diǎn)放在 SDK初始化、業(yè)務(wù)?具注冊(cè)、整體
didFinishLaunchingWithOptions ?法中,因?yàn)槲覀兊?些第三? ? ?app ?格配置、啟動(dòng)引導(dǎo)?顯示狀態(tài)邏輯、版本更新邏輯等等基本?都會(huì)在這?進(jìn)?,如果這部分邏輯沒有做好優(yōu)化梳理,隨著業(yè)務(wù)不斷拓展,臃腫的業(yè)務(wù)邏輯會(huì)直接導(dǎo)致啟動(dòng)時(shí) 間加?。
| 在滿?業(yè)務(wù)需求的前提下,盡量減少??didFinishLaunchingWithOptions??法在主線程中的事件處理邏輯,??如: 1.?根據(jù)實(shí)際業(yè)務(wù)狀況,梳理各個(gè)??/三?庫(kù),找到可以延遲加載的庫(kù),做延遲加載處理,?如放到??控制器?的viewDidAppear?法?。 2.?梳理業(yè)務(wù)邏輯,把可以延遲執(zhí)?的邏輯,做延遲執(zhí)?處理。?如檢查新版本、注冊(cè)推送通知等邏輯 3.?避免進(jìn)??些復(fù)雜/多余的計(jì)算邏輯,這類邏輯盡量進(jìn)?異步延遲處理 4.?避免在??控制器的viewDidLoad和viewWillAppear做太多容易阻塞主線程的事情,這2個(gè)?法執(zhí)?完,???控制器才能顯示 |
場(chǎng)景補(bǔ)充:
另外,在我們實(shí)際開發(fā)過(guò)程中,很多項(xiàng)?的??控制器都會(huì)有?些后臺(tái)可配、較為豐富的結(jié)構(gòu)或者推薦數(shù)據(jù) ?進(jìn)?展示,?且我們的??展示速度通常也會(huì)被納?啟動(dòng)優(yōu)化的?部分,其實(shí)對(duì)于這種類型的優(yōu)化,如果我 ?們還只是?傳統(tǒng)的 ?api -> data -> UI ?式進(jìn)?的話,就很難有明顯的改善空間,因?yàn)?戶的?絡(luò)狀態(tài) 并不是可控項(xiàng),如果不做其他處理的話,那在很多場(chǎng)景下對(duì)?戶來(lái)說(shuō),即使我們放上?些占位圖,展示的樣式也是很不友好的,畢竟??控制器對(duì)?戶的第?視覺沖擊影響還是?較?的。
對(duì)于這種場(chǎng)景下的優(yōu)化來(lái)說(shuō),?般我們可以采取 ?Local + Network + Update 的?式在?定程度上優(yōu)化 ??加載速度: ? ?即:
| 1、?app更新過(guò)程中,?先進(jìn)?本地內(nèi)嵌處理邏輯,內(nèi)嵌??數(shù)據(jù)結(jié)構(gòu)(?localDataBase)、內(nèi)嵌??樣式所需?資源(?localStorage) 2、在安裝啟動(dòng)之后,對(duì)本地與線上數(shù)據(jù)更新記錄進(jìn)?對(duì)?,檢測(cè)是否需要更新本地內(nèi)嵌數(shù)據(jù)結(jié)構(gòu) 3、檢測(cè)到有需要更新的數(shù)據(jù)時(shí),才會(huì)對(duì)指定結(jié)構(gòu)進(jìn)?靜默更新,并且同步更新本地?cái)?shù)據(jù)結(jié)構(gòu) |
這樣做的好處是:
| 1、??數(shù)據(jù)直接從本地加載,減少?絡(luò)數(shù)據(jù)等待時(shí)間 2、僅檢測(cè)數(shù)據(jù)key值變化,?數(shù)據(jù)量對(duì)?定向更新結(jié)構(gòu),減少api數(shù)據(jù)交互頻次及數(shù)據(jù)包體積 3、能夠保證??對(duì)于?戶來(lái)說(shuō)會(huì)?直處于?個(gè)友好的展示狀態(tài) |
當(dāng)然這種也并不是唯?的應(yīng)對(duì)?式,?且也并?對(duì)所有場(chǎng)景都適?,只是提供?種思路?已,還是需要根據(jù) 項(xiàng)?的實(shí)際場(chǎng)景選擇適合的優(yōu)化?案。
統(tǒng)計(jì)時(shí)?
另外如果在開發(fā)過(guò)程中,我們想直觀的查看 ?app 啟動(dòng)期間,各階段的耗時(shí)情況,也可以在Xcode,的 ?edit scheme 設(shè)置添加 ? DYLD_PRINT_STATISTICS 為1 ,打印啟動(dòng)時(shí)?,例如
優(yōu)化前啟動(dòng)時(shí)?:
優(yōu)化后啟動(dòng)時(shí)?:
當(dāng)然,這些log我們僅僅只能在開發(fā)調(diào)試階段查看打印,那么在實(shí)際項(xiàng)?中,我們需要對(duì)線上項(xiàng)?的啟動(dòng)數(shù)據(jù) 進(jìn)?監(jiān)控,以便及時(shí)的定位和優(yōu)化那些影響 ?app 啟動(dòng)時(shí)?的環(huán)節(jié),這時(shí)我們應(yīng)該怎樣更好的處理呢?
當(dāng)然我們可以通過(guò)服務(wù)器埋點(diǎn)上報(bào)的?式??統(tǒng)計(jì)分析,不過(guò)這樣?來(lái)會(huì)發(fā)現(xiàn)我們的統(tǒng)計(jì)成本就會(huì)??增 ? ? ?加,?且結(jié)果分析也會(huì)變得不那么靈活。所以這?推薦?種簡(jiǎn)單的監(jiān)控?式,那就是友盟的 ?U-APM 應(yīng)能性 能監(jiān)控SDK ,只需要我們進(jìn)?簡(jiǎn)單的pod集成之后,便可根據(jù)我們的實(shí)際需要進(jìn)??動(dòng)或者?動(dòng)監(jiān)控啟動(dòng)數(shù) ?據(jù),詳情可以參考 U-APM,?并且為了?便我們對(duì)數(shù)據(jù)進(jìn)?分析,友盟后臺(tái)已經(jīng)根據(jù)這些數(shù)據(jù)幫我們繪制出?了對(duì)應(yīng)的分布圖,我們可以??了然的得出啟動(dòng)耗時(shí)分布、啟動(dòng)類型占?等等,如圖:
除此之外,我們還可以通過(guò)SDK進(jìn)?崩潰分析、 ?ANR分析、監(jiān)控告警、卡頓分析、內(nèi)存分析等等諸多功能, 有了 ?U-APM 這個(gè)監(jiān)控平臺(tái),其實(shí)在實(shí)際開發(fā)過(guò)程中很?程度的提升了我們對(duì)線上 ?app 的優(yōu)化分析效率。
當(dāng)然本?的介紹也只是?較淺顯的優(yōu)化項(xiàng),僅供參考以及思路引導(dǎo),優(yōu)化之路任重?道遠(yuǎn),還需要我們不斷 的去探索、發(fā)現(xiàn)、提?。不過(guò)最后還是要提醒?句:在實(shí)際項(xiàng)?開發(fā)過(guò)程中,不要為了優(yōu)化?優(yōu)化,要根據(jù) 項(xiàng)?情況有針對(duì)性的進(jìn)?優(yōu)化。
參考:
探秘?Mach-O??件
iOS底層?- 從頭梳理?dyld 加載流程
iOSapp啟動(dòng)?- dyld加載App流程
wwdc2016optimizingappstartuptime.pdf
作者:武玉寶
原文鏈接
本文為阿里云原創(chuàng)內(nèi)容,未經(jīng)允許不得轉(zhuǎn)載。?
總結(jié)
以上是生活随笔為你收集整理的iOS App 启动优化的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 5分钟搞定Loki告警多渠道接入
- 下一篇: 如何基于LSM-tree架构实现一写多读