App啟動(dòng)優(yōu)化

我們?cè)谑褂媚承┏?jí)App時(shí),第一感受就是這款A(yù)pp怎么這么長(zhǎng)時(shí)間才啟動(dòng)起來(lái),等待的時(shí)間過(guò)長(zhǎng)可能我們自己就把App殺掉了,有的甚至可能沒(méi)有第二次啟動(dòng)機(jī)會(huì)就被卸載掉了,這個(gè)啟動(dòng)時(shí)間是非常影響用戶體驗(yàn)的。雖然我現(xiàn)在開發(fā)的App還不是超級(jí)App,啟動(dòng)時(shí)間也說(shuō)不上很慢,但是畢竟啟動(dòng)時(shí)間對(duì)于用戶來(lái)說(shuō)是越短越好,本著這個(gè)目標(biāo),我希望這次的優(yōu)化能夠更好的優(yōu)化App啟動(dòng)時(shí)間,給用戶帶來(lái)更好的用戶體驗(yàn)。

1、App啟動(dòng)類型

iPhone App的啟動(dòng)可以分為兩種類型:冷啟動(dòng)和熱啟動(dòng)。

1.1 冷啟動(dòng)

App啟動(dòng)前,其進(jìn)程已被殺死或根本就沒(méi)有在系統(tǒng)中存在,這種情況下點(diǎn)擊App圖標(biāo)啟動(dòng),就是一次冷啟動(dòng)過(guò)程。

1.2 熱啟動(dòng)

App啟動(dòng)后,將App切到后臺(tái),但是進(jìn)程沒(méi)有別殺死,進(jìn)程還在系統(tǒng)中存在,這種情況下重新點(diǎn)擊App圖標(biāo)進(jìn)入App,就是一次熱啟動(dòng)過(guò)程。

針對(duì)上述兩種啟動(dòng),我們一般優(yōu)化是指的優(yōu)化第一種類型的啟動(dòng)過(guò)程。

2、App啟動(dòng)過(guò)程

冷啟動(dòng)的過(guò)程就是從用戶點(diǎn)擊App圖標(biāo)開始,一直到- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions方法執(zhí)行完成為止。整個(gè)過(guò)程可以分為兩個(gè)階段,main()函數(shù)執(zhí)行之前和main()函數(shù)執(zhí)行之后。

app_launch_01.png

2.1 main()函數(shù)執(zhí)行之前

  1. 加載可執(zhí)行文件(App的.o文件集合)
  2. 加載動(dòng)態(tài)鏈接庫(kù),進(jìn)行rebase指針調(diào)整和bind符號(hào)綁定
  3. Objc運(yùn)行時(shí)的初始處理,包括Objc相關(guān)類的注冊(cè)、Category注冊(cè)、selector唯一性檢查等
  4. 初始化,包括執(zhí)行+load()方法、attribute((constructor))修飾的函數(shù)調(diào)用、創(chuàng)建C++靜態(tài)全局變量

備注:

  1. rebase指針調(diào)整:修復(fù)指向當(dāng)前鏡像內(nèi)部的資源指針。
  2. binding符號(hào)綁定:指向鏡像外部的資源指針
  3. image 二進(jìn)制文件,包括可執(zhí)行文件或so文件,里邊是被編譯過(guò)的符號(hào)、代碼等。
  4. imageLoader 將image加載進(jìn)內(nèi)存,且每一個(gè)文件對(duì)應(yīng)一個(gè)ImageLoader實(shí)例來(lái)負(fù)責(zé)加載。

dyld(動(dòng)態(tài)連接器,the dynamic link editor)是一個(gè)專門用來(lái)加載動(dòng)態(tài)鏈接庫(kù)的庫(kù),dyld從可執(zhí)行文件的依賴開始,遞歸加載所有依賴的動(dòng)態(tài)鏈接庫(kù)。動(dòng)態(tài)鏈接庫(kù)包括:iOS中用到的所有系統(tǒng)framework,加載OC runtime方法的libobjc,系統(tǒng)級(jí)別的libSystem

相應(yīng)的,這個(gè)階段對(duì)于啟動(dòng)速度優(yōu)化來(lái)說(shuō),可以優(yōu)化的點(diǎn)包含以下幾個(gè)方面:

  • 減少動(dòng)態(tài)庫(kù)加載。蘋果公司建議使用更少的動(dòng)態(tài)庫(kù),如果項(xiàng)目使用的動(dòng)態(tài)庫(kù)較多,可以將多個(gè)動(dòng)態(tài)庫(kù)合并使用,最多支持6個(gè)非系統(tǒng)動(dòng)態(tài)庫(kù)合為一個(gè)。
  • 減少objc類和selector數(shù)量,刪除啟動(dòng)后不會(huì)使用的類或者方法,合并分類文件,刪除無(wú)用依賴庫(kù)。
  • 將不必在+load()方法中做的事情延遲到+initialize()方法中。
  • 刪減無(wú)用靜態(tài)變量,控制C++全局變量的數(shù)量。

2.2 main()函數(shù)執(zhí)行之后

這個(gè)階段是指從main()函數(shù)執(zhí)行開始,到Appdelegate的application:didFinishLaunchingWithOptions:方法執(zhí)行完成。App的啟動(dòng)邏輯、首屏渲染和業(yè)務(wù)代碼都是在這個(gè)階段,主要包括:

  1. 全局初始化配置,各種組件庫(kù)的初始化(crash統(tǒng)計(jì)、埋點(diǎn)統(tǒng)計(jì))
  2. 首頁(yè)數(shù)據(jù)請(qǐng)求、獲取和處理
  3. 首頁(yè)UI計(jì)算和渲染

對(duì)于第一條,我們要梳理出哪些配置和初始化工作是首屏渲染必要的,哪些是App啟動(dòng)所要求的,除此之外的配置和初始化都分別滯后到合適的階段執(zhí)行即可。

優(yōu)化點(diǎn):

  • 梳理首屏渲染所不必須的依賴庫(kù)和功能邏輯,做延遲加載處理,比如放到首頁(yè)的- (void)viewDidAppear:(BOOL)animated方法中去執(zhí)行。
  • 在首頁(yè)控制器的- (void)viewDidLoad- (void)viewWillAppear:(BOOL)animated盡量的減少要做的事情,使首頁(yè)能夠盡快的加載顯示出來(lái)。
  • 不使用storyboardxib構(gòu)建首頁(yè)UI視圖,使用純代碼來(lái)加載首頁(yè)。

3、App啟動(dòng)優(yōu)化

說(shuō)了這么多,App啟動(dòng)優(yōu)化的時(shí)間如何來(lái)衡量呢,這就要用到上面提到的時(shí)間T1和T2,下面我們來(lái)說(shuō)下如何來(lái)計(jì)算T1和T2。

測(cè)試使用機(jī)器:iPhone 7 Plus,iOS 10.3.2系統(tǒng),存儲(chǔ)容量32GB。

3.1 T1

main()之前的時(shí)間T1蘋果官方提供了一種方法,在Xcode的Edit Scheme...-->Run-->Arguments選項(xiàng)中設(shè)置Environment Variables,添加name為DYLD_PRINT_STATISTICS,Value為1

app_launch_02.png

項(xiàng)目使用真機(jī)運(yùn)行,在控制臺(tái)就可以看到如下log了。

Total pre-main time: 4.2 seconds (100.0%)
         dylib loading time: 1.9 seconds (46.9%)
        rebase/binding time: 1.6 seconds (39.3%)
            ObjC setup time:  81.78 milliseconds (1.9%)
           initializer time: 498.71 milliseconds (11.7%)
           slowest intializers :
             libSystem.B.dylib :  19.39 milliseconds (0.4%)
                       AppName : 918.63 milliseconds (21.6%)

可以看到Total pre-main time總耗時(shí)為4.2秒。

3.2 T2

main()之后到- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions是T2階段的時(shí)間,我們可以采用記住頭尾時(shí)間取差值的方式得到T2,main函數(shù)代碼如下:

extern CFAbsoluteTime startTime;

bool isBeauifulStr(char *str);
int main(int argc, char * argv[]) {
    startTime = CFAbsoluteTimeGetCurrent();
    @autoreleasepool {
        
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([MIAppDelegate class]));
    }
}

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions方法中在最后加入如下代碼:

CFAbsoluteTime startTime;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    ...
        
    dispatch_async(dispatch_get_main_queue(), ^{
        NSUInteger ms = (NSUInteger)((CFAbsoluteTimeGetCurrent() - startTime) * 1000);
        NSLog(@"T2 = %lu ms", ms);
    });
 
    return YES;
}

項(xiàng)目啟動(dòng)后,輸出結(jié)果

T2 = 1190 ms

參考上面的優(yōu)化點(diǎn)逐條去對(duì)啟動(dòng)過(guò)程進(jìn)行優(yōu)化,然后對(duì)比得到的T1和T2的時(shí)間,就可以知道你的優(yōu)化效果到底如何了。

參考

iOS 程序 main 函數(shù)之前發(fā)生了什么
今日頭條iOS客戶端啟動(dòng)速度優(yōu)化
App 啟動(dòng)速度怎么做優(yōu)化與監(jiān)控?
優(yōu)化App的啟動(dòng)時(shí)間

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容