應(yīng)用啟動(dòng)時(shí)間,直接影響用戶(hù)對(duì)一款應(yīng)用的判斷和使用體驗(yàn)。所以App的啟動(dòng)優(yōu)化相對(duì)來(lái)說(shuō)在整個(gè)App的性能優(yōu)化中占有一定位置。
App啟動(dòng)概念
通常分為冷啟動(dòng)和熱啟動(dòng)
如果程序剛被運(yùn)行過(guò)一次,那么程序的代碼會(huì)被dyld緩存起來(lái),因此即使殺掉進(jìn)程再次重啟加載時(shí)間也會(huì)相對(duì)快一點(diǎn),如果長(zhǎng)時(shí)間沒(méi)有啟動(dòng)或者當(dāng)前dyld的緩存已經(jīng)被其他應(yīng)用占據(jù),那么這次啟動(dòng)所花費(fèi)的時(shí)間就要長(zhǎng)一點(diǎn),這就分別是熱啟動(dòng)和冷啟動(dòng)的概念。
App的完整啟動(dòng)流程(冷啟動(dòng))
1、main()?函數(shù)執(zhí)行前(pre-main階段)
2、main()?函數(shù)執(zhí)行后(從main函數(shù)執(zhí)行,到設(shè)置self.window.rootViewController執(zhí)行完成)
3、首屏渲染完成后(從self.window.rootViewController執(zhí)行完成到didFinishLaunchWithOptions方法作用域結(jié)束)
【通常又把2跟3當(dāng)做一塊】
pre-main階段做了什么
加載解析該APP的Info.plist文件,創(chuàng)建沙盒,根據(jù)Info.plist的配置檢查相應(yīng)權(quán)限狀態(tài)。
1、系統(tǒng)內(nèi)核創(chuàng)建一個(gè)進(jìn)程,然后加載可執(zhí)行文件(.o文件),讀取dyld路徑并運(yùn)行dyld動(dòng)態(tài)連接器、dyld是一個(gè)專(zhuān)門(mén)用來(lái)加載動(dòng)態(tài)鏈接庫(kù)的庫(kù),dyld從可執(zhí)行文件的依賴(lài)開(kāi)始, 遞歸加載所有的依賴(lài)動(dòng)態(tài)鏈接庫(kù)。
動(dòng)態(tài)鏈接庫(kù)包括:iOS 中用到的所有系統(tǒng) framework,加載OC runtime方法的libobjc,系統(tǒng)級(jí)別的libSystem,例如libdispatch(GCD)和libsystem_blocks (Block)。
以下是動(dòng)態(tài)鏈接庫(kù)加載的具體流程
2、讀取庫(kù)鏡像文件(load dylibs image)
3、進(jìn)行rebase指針調(diào)整(Rebase image)和bind符號(hào)綁定(Bind image)。
4、(Objc setup)ObjC的runtime初始化。 包括:ObjC相關(guān)Class的注冊(cè)、category注冊(cè)、selector唯一性檢查等。
5、(initializers)初始化。 包括:執(zhí)行Objc的+load()函數(shù),C++的構(gòu)造函數(shù)屬性函數(shù) 形如attribute((constructor)) ,非基本類(lèi)型的C++靜態(tài)全局變量的創(chuàng)建。
其中2、3、4屬于靜態(tài)調(diào)整(fix-up),到第5步開(kāi)始動(dòng)態(tài)調(diào)整,開(kāi)始在堆和堆棧中寫(xiě)入內(nèi)容。
上面整個(gè)事件由 dyld 主導(dǎo),完成運(yùn)行環(huán)境的初始化后,配合 ImageLoader 將二進(jìn)制文件按格式加載到內(nèi)存,動(dòng)態(tài)鏈接依賴(lài)庫(kù),并由 runtime 負(fù)責(zé)加載成 objc 定義的結(jié)構(gòu),所有初始化工作結(jié)束后,dyld 調(diào)用真正的 main 函數(shù)。
pre-main階段可做的優(yōu)化
減少不必要的framework,因?yàn)閯?dòng)態(tài)鏈接比較耗時(shí)
能選擇靜態(tài)庫(kù)就選擇靜態(tài)庫(kù),少用動(dòng)態(tài)庫(kù),必須依賴(lài)動(dòng)態(tài)庫(kù),則把多個(gè)非系統(tǒng)的動(dòng)態(tài)庫(kù)合并成一個(gè)動(dòng)態(tài)庫(kù)
check framework應(yīng)當(dāng)設(shè)為optional和required,optional會(huì)有些額外的檢查會(huì)導(dǎo)致加載變慢
合并或者刪減一些OC類(lèi),使用工具AppCode代碼檢查功能清理項(xiàng)目中沒(méi)用到的類(lèi)
減少項(xiàng)目文件中Category,刪減一些無(wú)用的靜態(tài)變量、刪減沒(méi)有被調(diào)用到或者已經(jīng)廢棄的方法
將不必須在+load方法中做的事情延遲到+initialize(一般用于初始化全局變量或靜態(tài)變量)?類(lèi)第一次被加載時(shí)調(diào)用+initialize
盡量不要用C++虛函數(shù)
main()調(diào)用之后的加載(APP初始化流程)
在main()被調(diào)用之后,App的主要工作就是初始化必要的服務(wù),顯示首頁(yè)內(nèi)容等。
App通常在AppDelegate類(lèi)中的didFinishLaunchingWithOptions方法中創(chuàng)建首頁(yè)需要展示的view,然后在當(dāng)前runloop的末尾,主動(dòng)調(diào)用CA::Transaction::commit完成視圖的渲染
APP初始化流程的優(yōu)化點(diǎn)
1.盡量使用純代碼而不是xib或者storyboard來(lái)進(jìn)行UI框架的搭建,尤其是使用的TabBarController這種,盡量避免使用xib和storyboard,因?yàn)閤ib和storyboard也還是要解析成代碼來(lái)渲染頁(yè)面,并且官網(wǎng)為了滿足更多的需求,必定做了更多的適配判斷處理,會(huì)多很多步驟。會(huì)增加代碼的執(zhí)行效率從而增加啟動(dòng)時(shí)長(zhǎng)。
2.盡量在application:didFinishLaunchingWithOptions:中代碼的執(zhí)行時(shí)間。能多線程就多線程,能后臺(tái)執(zhí)行就后臺(tái)執(zhí)行。部分加載可以選擇懶加載或者后臺(tái)加載。不要阻塞主線程從而造成啟動(dòng)時(shí)間加長(zhǎng)。
別的相關(guān)
ViewController的生命周期方法
1、initWithCoder(用sb)或initWithNibName(用xib或純代碼)
2、awakeFromNib ( xib加載完成時(shí)調(diào)用,純代碼不會(huì)調(diào)用)
3、loadView(重寫(xiě)可以不調(diào)用父類(lèi)的方法[super loadView])
[super loadView]會(huì)創(chuàng)建一個(gè)空白視圖(純代碼下,如果自己本身就需要?jiǎng)?chuàng)建視圖,那么無(wú)需調(diào)用[super loadView],造成了一些不必要的開(kāi)銷(xiāo))
4、viewDidLoad(各種初始化操作)
5、viewWillAppear(視圖將要出現(xiàn))
6、viewWillLayoutSubviews (需要調(diào)整view的Subviews子視圖的位置)
觸發(fā)viewWillLayoutSubviews的幾種情形
1、addSubview會(huì)觸發(fā)viewWillLayoutSubviews
2、設(shè)置self.view及子視圖的frame.size會(huì)觸發(fā)layoutSubviews,當(dāng)然前提是frame.size的值設(shè)置前后發(fā)生了變化,注意,此處不是origin,呼應(yīng)官方文檔上的邊界發(fā)生變化
3、滾動(dòng)一個(gè)UIScrollView(該scrollview有子視圖的時(shí)候)會(huì)觸發(fā)layoutSubviews
4、橫豎屏幕切換會(huì)觸發(fā)
7、viewDidLayoutSubviews(調(diào)整完成之后需要做的工作)
8、viewDidAppear(視圖已經(jīng)出現(xiàn))
9、viewWillDisappear(視圖即將消失)
10、viewDidDisappear(視圖消失)
11、dealloc (釋放掉init與viewDidLoad中創(chuàng)建的對(duì)象)
12、didReceiveMemoryWarning (內(nèi)存警告)