App
啟動分為冷啟動和熱啟動,我們說的啟動優(yōu)化一般是指冷啟動優(yōu)化锹雏。若要想優(yōu)化巴比,首先我們必須明確啟動過程。
啟動過程分為兩個階段
-
main
函數(shù)前 -
main
函數(shù)后
main
函數(shù)前
可以通過添加Xcode
環(huán)境變量DYLD_PRINT_STATISTICS
來打印pre-main
花費的時間
如果想查看更加詳細的信息再添加環(huán)境變量DYLD_PRINT_STATISTICS_DETAILS
Total pre-main time: 5.0 seconds (100.0%)
dylib loading time: 3.6 seconds (72.2%)
rebase/binding time: 1.1 seconds (22.9%)
ObjC setup time: 100.24 milliseconds (1.9%)
initializer time: 139.23 milliseconds (2.7%)
slowest intializers :
libSystem.B.dylib : 5.24 milliseconds (0.1%)
執(zhí)行流程
- 加載可執(zhí)行文件
- 加載動態(tài)庫由于使用了
ASLR
(Adress Space Layout Randomization
)rebase
[調(diào)整鏡像內(nèi)指針偏移]binding
[調(diào)整對鏡像外符號信用偏移]礁遵;一般來說動態(tài)庫會加載100
-400
個大多數(shù)為OS dylib
(系統(tǒng)動態(tài)庫)轻绞,相對來說OS dylib
在加載時做了優(yōu)化調(diào)整 - 初始化
runtime
:包括,類注冊榛丢,屬性動態(tài)調(diào)整铲球,方法唯一性驗證,分類中方法插入到類方法中 -
C++
靜態(tài)變量初始化晰赞,構造函數(shù)attribute(constructor)
初始化
優(yōu)化方案
- 減少動態(tài)庫加載:蘋果支持
6
個非系統(tǒng)動態(tài)庫合并
合并動態(tài)庫:
lipo -create path/yourFramework1 path/yourFramework2 -output path/yourFramework
合并靜態(tài)庫:
lipo -create '/sim/lib.a' '/dev/lib.a' -output 'lib.a'
-
減少
ObjC
類(class
)稼病,方法(selector
),分類(category
)的數(shù)量ObjC
因為支持了動態(tài)語言掖鱼,內(nèi)部會維護一個記錄類名與類關系的表然走,如果類數(shù)量多就會造成表很大- 移除不用的類
// 安裝fui工具,在終端中執(zhí)行命令 官網(wǎng):https://github.com/dblock/fui sudo gem install fui -n /usr/local/bin // 使用, 查找當前目錄下未使用的類;注意查找出的內(nèi)容并不一定靠譜戏挡,還需要自己在XCode中驗證 fui find
// 使用otool命令可查看__DATA.__objc_classrefs段和__DATA.__objc_classlist段芍瑞,兩者的差集可以認為是定義了但未使用的類。 // 1. 獲取可執(zhí)行文件下所有OC類名 otool -v -s __DATA __objc_classlist 可執(zhí)行文件名 // 2. 獲取可執(zhí)行文件所有引用到的OC類名 otool -v -s __DATA __objc_classrefs 可執(zhí)行文件名 // 3. 兩者的差集就是代碼中沒有直接使用到的OC類褐墅,不過需要注意反射機制使用類名來操作的情況拆檬,需要人工做下篩選 // 4. 獲取所有符號,可以取出地址與符號對應關系 nm -nm 可執(zhí)行文件名 // 5. 如果一個子類實例化妥凳,父類未實例化竟贯,那么父類不會出現(xiàn)在__objc_classrefs這個段里。需要將移除未使用的這部分OC類 // 可以獲取到類的繼承關系 otool -oV 可執(zhí)行文件名 參考資料:https://juejin.im/post/5d5d1a92e51d45620923886a 參考腳本:https://github.com/xuezhulian/classunref
- 合并分類
- 將
load
方法中的內(nèi)容推遲到initialize
中操作
放到initialize方法中逝钥,并適時使用dispatch_once來保證執(zhí)行效果
- 減少
C++
虛函數(shù)的數(shù)量
創(chuàng)建虛函數(shù)表有開銷屑那;
- 減少
C/C++
中構造器,非基本類型的常量
C/C++的構造器函數(shù)(用attriubte((constructor))修飾的函數(shù))艘款,和創(chuàng)建非基本類型的C++靜態(tài)全局變量(通常是類或結(jié)構體)
- 使用
Swift struct
內(nèi)部做了優(yōu)化持际,符號數(shù)量更少
- 二進制重排,減少
page fault
產(chǎn)生
請求分頁系統(tǒng)中每當要訪問的頁面不存在是哗咆,便會觸發(fā)一個缺頁中斷(
page fault
)蜘欲,然后操作系統(tǒng)就會阻塞這個進程,晌柬,直到調(diào)頁完成后芒填,才會重新執(zhí)行呜叫。由此如果發(fā)生缺頁中斷次數(shù)太多就會耗時較多。我們此處只考慮啟動過程中
缺頁中斷視頻講解
https://www.bilibili.com/video/av86534144?p=6
重排的目的就是為了在啟動過程中盡可能少的觸發(fā)page fault殿衰。
查看當前App
啟動產(chǎn)生多少次page fault
.
1. 通過Profile -> System Trace
2. 選擇真機朱庆,選擇工程,點擊啟動闷祥,當首個頁面加載出來就停止
3. 選擇主線程娱颊,-> 選擇 Summary:Virtual Memory 其中File Backed Page In Count 即為pageFault次數(shù)
方案:
1. 構建一個orderFile.order;有序文件
2. 在Xcode Build settings中搜索Order File凯砍,配置orderfile路徑
- 構建有序文件
本質(zhì)是通過全局的
AOP
箱硕,Hook
所有方法
使用AppOrderFiles
工具進行操作
https://github.com/yulingtianxia/AppOrderFiles
詳細理解方案
http://www.zyiz.net/tech/detail-127196.html
main函數(shù)后
執(zhí)行流程
- 首屏初始化所需配置文件的讀寫操作
- 首屏列表數(shù)據(jù)的讀取
- 首屏渲染的大量計算等
優(yōu)化方案
梳理功能調(diào)整調(diào)用時機
a. 可以延遲加載的庫延遲
b. 復雜耗時計算,延遲或者開線程
c. 首頁控制器最好使用純代碼方式
常用策略
分析link map
文件
link map
是編譯連接時生成的一個txt
文件悟衩,它生成的目的就是幫助程序員分析包大小剧罩。
link map
記錄了每個方法在當前二進制架構下占據(jù)的空間。通過分析link map
座泳,我們可以了解到每個類甚至每個方法占據(jù)了多少安裝包空間惠昔。
使用方式如下
// 1. 開啟
Xcode build setting 中開啟Write Link Map
// 2. 配置Linkmap路徑
path to link map
總結(jié)
大概闡述導致啟動慢的原因,及解決方案挑势;
針對比較深入的知識點镇防,也查看了不少資料,作為切入理解的點潮饱。作為后續(xù)深入研究的方向来氧。
參考及延伸:
http://www.reibang.com/p/b19cd03eea68
http://yulingtianxia.com/blog/2019/09/01/App-Order-Files/
http://www.zyiz.net/tech/detail-127196.html
https://developer.apple.com/library/archive/documentation/Performance/Conceptual/CodeFootprint/Articles/ImprovingLocality.html#//apple_ref/doc/uid/20001862-117091-BCIBJEBH