服務(wù)器斷電了闸天,所以得空來寫點(diǎn)東西,之所以寫這個(gè)問題是因?yàn)橐淮螢觚埵录弊觯晃覀冺?xiàng)目上線引發(fā)了看門狗超時(shí)問題苞氮,就是那個(gè)eatBadfood,然后導(dǎo)致啟動(dòng)時(shí)間超時(shí)瓤逼,高達(dá)27s笼吟,呵呵,啥玩意27s起不來啊抛姑,出問題設(shè)備是iPad但是我們測試時(shí)候沒有遇到。
OK艳狐,那就是我的問題了吧定硝,不多說啟動(dòng)超時(shí)我們就來處理啟動(dòng)超時(shí)的問題。
首先就是需要了解關(guān)于app啟動(dòng)的時(shí)候都干了什么事情毫目,包括我們點(diǎn)擊build按鈕都干了什么:
當(dāng)我們點(diǎn)擊了 build 之后蔬啡,做了什么事情呢?
預(yù)處理(Pre-process):把宏替換镀虐,刪除注釋箱蟆,展開頭文件,產(chǎn)生 .i 文件刮便。
編譯(Compliling):把之前的 .i 文件轉(zhuǎn)換成匯編語言空猜,產(chǎn)生 .s文件。
匯編(Asembly):把匯編語言文件轉(zhuǎn)換為機(jī)器碼文件,產(chǎn)生 .o 文件辈毯。
鏈接(Link):對.o文件中的對于其他的庫的引用的地方進(jìn)行引用坝疼,生成最后的可執(zhí)行文件(同時(shí)也包括多個(gè) .o 文件進(jìn)行 link)。
其實(shí)通常對我們來說的話就是分為編譯階段和執(zhí)行階段谆沃;在我們app代碼里面進(jìn)行區(qū)分就是以main()函數(shù)為間隔區(qū)分這兩個(gè)階段钝凶,main()函數(shù)執(zhí)行之前做的事情就是我們所要進(jìn)行優(yōu)化的地方了,即pre-main:在這段時(shí)間里系統(tǒng)會(huì)進(jìn)行加載動(dòng)態(tài)庫唁影、注冊 Objc 類等系統(tǒng)操作耕陷。
在我們要優(yōu)化之前首先我們要看一下,我們具體看一下啟動(dòng)時(shí)間到底是多少(系統(tǒng)方法):Edit scheme -> Run -> Auguments 將環(huán)境變量 DYLD_PRINT_STATISTICS 設(shè)為 1)然后運(yùn)行据沈,控制臺(tái)就會(huì)打印我們的premain所用的時(shí)間哟沫。
Total pre-main time: 915.59 milliseconds (100.0%)
?? ? ? ? dylib loading time: 499.70 milliseconds (54.5%)
? ? ? ? rebase/binding time:? 48.53 milliseconds (5.3%)
? ? ? ? ? ? ObjC setup time:? 24.47 milliseconds (2.6%)
?? ? ? ? ? initializer time: 342.87 milliseconds (37.4%)
?? ? ? ? ? slowest intializers :
?? ? ? ? ? ? libSystem.B.dylib :? 4.06 milliseconds (0.4%)
? ? libMainThreadChecker.dylib :? 34.28 milliseconds (3.7%)
? ? ? ? ? libglInterpose.dylib : 124.91 milliseconds (13.6%)
? ? ? ? ? ? ? ? ? ? ? ? Pretty : 263.99 milliseconds (28.8%)
? total time: 1.8 seconds (100.0%)
? total images loaded:? 573 (523 from dyld shared cache)
? total segments mapped: 195, into 13129 pages
? total images loading time: 1.2 seconds (66.3%)
? total load time in ObjC:? 24.47 milliseconds (1.2%)
? total debugger pause time: 759.18 milliseconds (40.0%)
? total dtrace DOF registration time:? 0.00 milliseconds (0.0%)
? total rebase fixups:? 279,519
? total rebase fixups time:? 40.52 milliseconds (2.1%)
? total binding fixups: 720,435
? total binding fixups time: 222.74 milliseconds (11.7%)
? total weak binding fixups time:? 8.30 milliseconds (0.4%)
? total redo shared cached bindings time: 223.03 milliseconds (11.7%)
? total bindings lazily fixed up: 0 of 0
? total time in initializers and ObjC +load: 342.87 milliseconds (18.0%)
?? ? ? ? ? ? ? ? ? ? ? ? libSystem.B.dylib :? 4.06 milliseconds (0.2%)
?? ? ? ? ? ? ? libBacktraceRecording.dylib :? 5.84 milliseconds (0.3%)
? ? ? ? ? ? ? ? libMainThreadChecker.dylib :? 34.28 milliseconds (1.8%)
? ? ? ? ? ? ? ? ? ? ? libglInterpose.dylib : 124.91 milliseconds (6.5%)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? CoreDuet :? 2.50 milliseconds (0.1%)
?? ? ? ? ? ? ? ? ? ? ? libMTLCapture.dylib :? 2.71 milliseconds (0.1%)
? ? ? ? ? ? ? libViewDebuggerSupport.dylib :? 8.01 milliseconds (0.4%)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? FBSDKCoreKit :? 4.42 milliseconds (0.2%)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Pretty : 263.99 milliseconds (13.9%)
total symbol trie searches:? ? 1703506
total symbol table binary searches:? ? 0
total images defining weak symbols:? 59
total images using weak symbols:? 136
其實(shí)看起來還是很亂七八糟的,因?yàn)槲以趀nvironment里添加了另外一行:DYLD_PRINT_STATISTICS_DETAILS為YES卓舵,然后打印的就如上這么詳細(xì)了南用。
反正我當(dāng)時(shí)看的時(shí)候是一臉懵逼,這都是啥掏湾,多虧了有大神指點(diǎn)裹虫,我們可以來詳細(xì)的分析:
pre-main時(shí)間主要由 4 部分組成(原文搬運(yùn)):
1.dylib loading:
這一階段 dyld 會(huì)分析應(yīng)用依賴的 dylib ,所以融击,依賴的 dylib 越少越好筑公。在這一步,我們能做的優(yōu)化就是檢查是否存在不需要的 dylib 尊浪,移除不必要的 dylib 匣屡。
在項(xiàng)目優(yōu)化實(shí)踐中,我們移除了一個(gè)沒有必要的動(dòng)態(tài)庫拇涤,并將幾個(gè)動(dòng)態(tài)庫合成為一個(gè)動(dòng)態(tài)庫捣作,減少動(dòng)態(tài)庫數(shù)量。
2.rebase/binding:
這一階段系統(tǒng)主要注冊 Objc 類鹅士。所以券躁,指針數(shù)量越少越好。這一步能做的優(yōu)化有:
清理項(xiàng)目中無用的類
刪減沒有被調(diào)用到或者已經(jīng)廢棄的方法
刪減一些無用的靜態(tài)變量
可通過 AppCode 等工具掃描項(xiàng)目中未使用的代碼掉盅。
3.Objc srtup:
這一階段沒有什么特別能優(yōu)化的地方也拜,如果 rebase/binding 階段優(yōu)化的好這步耗時(shí)也會(huì)很少。
4.initializer:
這一階段趾痘,dyld 開始運(yùn)行程序的初始化函數(shù)慢哈,調(diào)用每個(gè) Objc 類和分類的 +load 方法,調(diào)用 C/C++ 中的構(gòu)造器函數(shù)永票。initializer階段執(zhí)行完后卵贱,dyld 開始調(diào)用 main() 函數(shù)滥沫。在這一步,檢查 +load 方法艰赞,盡量把事情推遲到 +initiailize 方法里執(zhí)行佣谐。
在這里我們修改了部分原本代碼中直接在 +load 函數(shù)初始化邏輯改為在 +initialize 中加載,也就是到使用時(shí)才加載方妖。
參考以上的教程狭魂,我尋找了我們app的需要優(yōu)化的地方,拼命減少了一個(gè)動(dòng)態(tài)庫党觅,還有幾個(gè)三方庫雌澄,把一些用來測試的指針都給干掉了,當(dāng)然因?yàn)槭切碌腶pp杯瞻,所以沒有用app code檢測镐牺,以后可以使用試試。
雖然我沒看出來啟動(dòng)時(shí)間有啥進(jìn)步魁莉,但是改了就是進(jìn)步了安墙А;美滋滋提交旗唁,求神拜佛畦浓,結(jié)果同樣問題,同樣結(jié)果检疫。
我就開始總結(jié)讶请,這個(gè)問題一開始就和啟動(dòng)時(shí)間沒有關(guān)系啊,再超時(shí)也不可能27s嘛屎媳,第二次打回來告訴我32s夺溢,這就更離譜了,所以我進(jìn)行了錯(cuò)誤日志解析烛谊,呵呵风响,原來是三方庫的問題(百度地圖sdk導(dǎo)致);所以建議在finishLaunch的時(shí)候部分三方注冊丹禀,代理的內(nèi)容可以放到子線程做状勤。
我們回過頭來看啟動(dòng)時(shí)間的問題,我們通過上面對照能夠看懂我們程序所打印的內(nèi)容湃崩,如果能夠獲取程序在各個(gè)頁面顯示出來所占用的時(shí)間荧降,然后我們才能進(jìn)行具體優(yōu)化接箫,判斷是否有我們?nèi)庋鬯床坏降目D問題攒读。
下面是正文:
1.premain()以后的顯示的頁面時(shí)間優(yōu)化
? ? (1).在我們首頁顯示之前,盡量減少操作辛友。
? ? 例如:各種網(wǎng)絡(luò)請求薄扁,能省就省了吧剪返;能用代碼構(gòu)建UI,盡量少用xib邓梅;手勢等添加可以在頁面顯示之后脱盲;減少加載的view controller的加載數(shù)量;部分可以延時(shí)處理的操作可以放到子線程日缨。
? ? 至于使用動(dòng)畫钱反,個(gè)人覺得就算了吧,如果不是特別熟練清楚底層原理匣距,建議不要使用(動(dòng)畫不會(huì)被主線程阻塞)
? ? (2).頁面卡頓的優(yōu)化
? ? 這里感覺能優(yōu)化的就有很多了面哥,可以通過bugly來監(jiān)測一些卡頓問題,通過具體代碼去解決問題毅待。
? ? 還有關(guān)于CPU和GPU尚卫,頁面渲染相關(guān)的問題我也正在學(xué)習(xí)階段,建議大家多讀大神文章尸红,一起學(xué)習(xí)吧吱涉。畢竟我也是菜的不行。
2.頁面加載耗時(shí)打印
? ? 打印各個(gè)頁面加載時(shí)間,有助于我們發(fā)現(xiàn)很多平時(shí)看不到的問題蚤假,對比得到哪些頁面需要去優(yōu)化悯森。
? ? 下面我來介紹一下具體方法:
? ? 我們想要了解加載頁面的view消耗的時(shí)間需要知道VC的生命周期:
? ??1.initWithCoder: 通過 nib 文件初始化時(shí)觸發(fā)。
????2.awakeFromNib: nib 文件被加載的時(shí)候疙咸,會(huì)發(fā)生一個(gè) awakeFromNib 的消息到 nib 文件中的每個(gè)對象。
????3.loadView: 開始加載視圖控制器自帶的 view风科。
????4.viewDidLoad: 視圖控制器的 view 被加載完成撒轮。
????5.viewWillAppear: 視圖控制器的 view 將要顯示在 window 上。
????6.updateViewConstraints: 視圖控制器的 view 開始更新 AutoLayout 約束贼穆。????????????
????7.viewWillLayoutSubviews:視圖控制器的 view 將要更新內(nèi)容視圖的位置题山。
????8.viewDidLayoutSubviews: 視圖控制器的 view 已經(jīng)更新視圖的位置。
????9.viewDidAppear: 視圖控制器的 view 已經(jīng)展示到 window 上故痊。
????10.viewWillDisappear: 視圖控制器的 view 將要從 window 上消失顶瞳。
????11.viewDidDisappear: 視圖控制器的 view 已經(jīng)從 window 上消失。
? ? 所以愕秫,我們只需要在loadview的時(shí)候打印一下當(dāng)前時(shí)間[[NSDate date] timeIntervalSince1970]慨菱,因?yàn)楂@取到的時(shí)間單位是秒,建議*1000換算成毫秒戴甩,看起來比較直觀符喝。在viewDidAppear里面再打印一次當(dāng)前時(shí)間,兩個(gè)時(shí)間的差值就是頁面的加載時(shí)間啦甜孤,如果加載時(shí)間太久协饲,那就需要進(jìn)行優(yōu)化了畏腕。(也得根據(jù)自己的代碼具體細(xì)節(jié)進(jìn)行調(diào)整,建議在loadview進(jìn)行UI布局)
剛剛打印了一下時(shí)間:當(dāng)前開始加載1577944446238
當(dāng)前結(jié)束加載1577944446251
這不是扯么茉稠,速度這么快描馅?我都不信,但是這是事實(shí)而线,這是沒有進(jìn)行網(wǎng)絡(luò)加載的铭污,單純的加載UI的時(shí)間,不包括刷新方法膀篮;你需要在你需要的地方進(jìn)行布局埋點(diǎn)况凉,統(tǒng)計(jì)這個(gè)頁面的加載時(shí)間。
有更好的方法的小伙伴各拷,歡迎留言刁绒。
參考:http://www.reibang.com/p/6387eba2ea16