1. 程式人生 > >【ios學習】優化 App 的啟動時間實踐 iOS

【ios學習】優化 App 的啟動時間實踐 iOS

前言

當用戶按下home鍵的時候,iOS的App並不會馬上被kill掉,還會繼續存活若干時間。理想情況下,使用者點選App的圖示再次回來的時候,App幾乎不需要做什麼,就可以還原到退出前的狀態,繼續為使用者服務。這種持續存活的情況下啟動App,我們稱為熱啟動,相對而言冷啟動就是App被kill掉以後一切從頭開始啟動的過程。我們這裡只討論App冷啟動的情況。

對於冷啟動來說,啟動時間是指從使用者點選 APP 那一刻開始到使用者看到第一個介面這中間的時間。我們進行優化的時候,我們將啟動時間分為 pre-main 時間和 main 函式到第一個介面渲染完成時間這兩個部分。

因為 APP 的入口在 main 函式 ,在 main 函式之後我們的程式碼才會執行。

這裡有兩個階段

1. pre-main階段

1.1. 載入應用的可執行檔案

1.2. 載入動態連結庫載入器dyld(dynamic loader)

1.3. dyld遞迴載入應用所有依賴的dylib(dynamic library 動態連結庫)

2. main()階段

2.1. dyld呼叫main() 

2.2. 呼叫UIApplicationMain() 

2.3. 呼叫applicationWillFinishLaunching

2.4. 呼叫didFinishLaunchingWithOptions

我們把 pre-main階段稱為 t1,main()階段一直到首個頁面載入完成稱為 t2。

t1 時間的優化分析

t1部分主要參考自APP啟動優化的一次實踐

其中 t1蘋果提供了內建的測量方法, Xcode 中 Edit scheme -> Run -> Auguments 將環境變數 DYLD_PRINT_STATISTICS 設為 1

123456789//結果為Total pre-main time: 1.4seconds (100.0%)dylib loading time: 1.3seconds (89.4%)rebase/binding time:  36.75milliseconds (2.5%)ObjC setup time:  35.65milliseconds (2.4%)initializer time:  
80.97milliseconds (5.5%)slowest intializers :libSystem.B.dylib :  12.63milliseconds (0.8%)//解讀

1、main()函式之前總共使用了1.4s

2、在94.33ms中,載入動態庫用了1.3s,指標重定位使用了36.75ms,ObjC類初始化使用了35.65ms,各種初始化使用了80.97ms。

3、在初始化耗費的80.97ms中,用時最多的初始化是libSystem.B.dylib。

可以看到,我的 dylib loading time 花費了 1.3s時間,

其中各部分的作用是

載入dylib

分析每個dylib(大部分是iOS系統的),找到其Mach-O檔案,

開啟並讀取驗證有效性,找到程式碼簽名註冊到核心,

最後對dylib的每個segment呼叫mmap()。

rebase/bind

dylib載入完成之後,它們處於相互獨立的狀態,需要繫結起來。

在dylib的載入過程中,系統為了安全考慮,引入了ASLR(Address Space Layout Randomization)技術和程式碼簽名。

由於ASLR的存在,映象(Image,包括可執行檔案、dylib和bundle)會在隨機的地址上載入,和之前指標指向的地址(preferred_address)會有一個偏差(slide),dyld需要修正這個偏差,來指向正確的地址。

Rebase在前,Bind在後,Rebase做的是將映象讀入記憶體,修正映象內部的指標,效能消耗主要在IO。

Bind做的是查詢符號表,設定指向映象外部的指標,效能消耗主要在CPU計算。

OC setup

OC的runtime需要維護一張類名與類的方法列表的全域性表。

dyld做了如下操作:

對所有宣告過的OC類,將其註冊到這個全域性表中(class registration)

將category的方法插入到類的方法列表中(category registration)

檢查每個selector的唯一性(selector uniquing)

如果在各個 OC 類別的 ‘load’方法裡做了不少事情(如在裡面使用 Method swizzle),那麼這是pre-main階段最耗時的部分。dyld執行APP的初始化函式,呼叫每個OC類的+load方法,呼叫C++的構造器函式(attribute((constructor))修飾),建立非基本型別的C++靜態全域性變數,然後執行main函式。

優化思路是

1. 移除不需要用到的動態庫

2. 移除不需要用到的類

3. 合併功能類似的類和擴充套件

4. 儘量避免在+load方法裡執行的操作,可以推遲到+initialize方法中。

t2 時間的優化分析

t2使用了來自NewPan大大 的打點計時器BLStopwatch

檢測耗時

可以看到,我的 APP 載入時間並沒有很慢,但是也想看一看有沒有優化的空間。

在 didFinishLaunchingWithOptions 方法裡我們一般都有以下的邏輯:

初始化第三方 SDK

配置 APP 執行需要的環境

自己的一些工具類的初始化

...

這裡主要參考[iOS]一次立竿見影的啟動時間優化

從優化圖可以看到,我的應用的跳轉邏輯是 開啟 -> 廣告頁 -> 首頁,首頁的UI 架構是:

UITabBarC管理一堆 UINavigationC

但是如果 UI 架構如上,並且在didFinishLaunchingWithOptions裡面設定了根檢視

123456789101112- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {NSLog(@"didFinishLaunchingWithOptions 開始執行");self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];TestTabBarController *tabBarVc = [TestTabBarController new];self.window.rootViewController = tabBarVc;[self.window makeKeyAndVisible];NSLog(@"didFinishLaunchingWithOptions 跑完了");returnYES;}

然後我們來到 TestTabBarController 裡的 viewDidLoad方法裡進行它的 viewControllers 的設定,然後再進入到每個 viewController 的 viewDidLoad 方法裡進行更多的初始化操作。那麼你覺得從 didFinishLaunchingWithOptions 到最後顯示展示的 viewController 的 viewDidLoad 這些方法的執行順序是怎麼樣的呢?

didFinishLaunchingWithOptions 開始執行 

開始載入 TestTabBarController 的 viewDidLoad

didFinishLaunchingWithOptions 跑完了

開始載入 TestViewController 的 viewDidLoad, 然後執行一堆初始化的操作

在TestTabBarController 中操作了 TestViewController 的 view 的話,那麼呼叫順序將會是這樣:

didFinishLaunchingWithOptions 開始執行 

開始載入 TestTabBarController 的 viewDidLoad

開始載入 TestViewController 的 viewDidLoad, 然後執行一堆初始化的操作

didFinishLaunchingWithOptions 跑完了

這樣的問題就是當我們把介面的初始化、網路請求、資料解析、檢視渲染等操作放在了viewDidLoad 方法裡,這樣一來每次啟動 APP 的時候,在使用者看到第一個頁面之前,我們要把這些事件全部都處理完,才會進入到檢視渲染階段。

一般來說,我們放到didFinishLaunchingWithOptions執行的程式碼,有很多初始化操作,如日誌,統計,SDK配置等。儘量做到只放必需的,其他的可以延遲到MainViewController展示完成viewDidAppear以後。

* 日誌、統計等必須在 APP 一啟動就最先配置的事件

* 專案配置、環境配置、使用者資訊的初始化 、推送、IM等事件

* 其他 SDK 和配置事件

  • 第一類,必須第一時間啟動,仍然把它留在 didFinishLaunchingWithOptions 裡啟動。

  • 第二類,這些功能在使用者進入 APP 主體的之前是必須要載入完的,我把他放到廣告頁面的viewDidAppear啟動。

  • 第三類,由於啟動時間不是必須的,所以我們可以放在第一個介面的 viewDidAppear 方法裡,這裡完全不會影響到啟動時間。

優化後

這是優化後的啟動時間

優化思路

梳理各個三方庫,找到可以延遲載入的庫,做延遲載入處理,比如放到首頁控制器的viewDidAppear方法裡。

梳理業務邏輯,把可以延遲執行的邏輯,做延遲執行處理。比如檢查新版本、註冊推送通知等邏輯。

避免複雜/多餘的計算。

避免在首頁控制器的viewDidLoad和viewWillAppear做太多事情,這2個方法執行完,首頁控制器才能顯示,部分可以延遲建立的檢視應做延遲建立/懶載入處理。

採用效能更好的API。

首頁控制器用純程式碼方式來構建。

另:[iOS]一次立竿見影的啟動時間優化 提到了使用一個工具類來管理的方法,可以比較方便的管理優化。

總結

相關推薦

ios學習優化 App啟動時間實踐 iOS

前言當用戶按下home鍵的時候,iOS的App並不會馬上被kill掉,還會繼續存活若干時間。理想情況下,使用者點選App的圖示再次回來的時候,App幾乎不需要做什麼,就可以還原到退出前的狀態,繼續為使用者服務。這種持續存活的情況下啟動App,我們稱為熱啟動,相對而言冷啟動就是

iOS開發判斷app啟動的方式(launchOptions)

iOS app啟動的方式有哪些: 自己啟動(使用者手動點選啟動)urlscheme啟動(關於urlScheme的詳解點選開啟連結)本地通知啟動  (自己寫的本地通知啟動,藍芽模組的啟動,地理圍欄的啟動)遠端通知啟動    (後臺伺服器的推送通知)在appdelegate.m

王小草機器學習筆記--主題模型LDA實踐與應用

標籤(空格分隔): 王小草機器學習筆記 筆記整理時間:2016年12月30日 筆記整理者:王小草 1. LDA的實現工具 在主題模型LDA的理論篇,長篇大幅的公式與推導也許實在煩心,也不願意自己去寫程式碼實現一遍的話,不妨用一用一些已經開源和

Spark2.0源碼學習-6.Client啟動

rms permsize wrapper 2.0 proxy waiting 默認 說明 加載器 Client作為Endpoint的具體實例,下面我們介紹一下Client啟動以及OnStart指令後的額外工作 一、腳本概覽 下面是一個舉例: /opt

.netcore學習.netcore添加到 supervisor 守護進程自啟動報錯

comm 配置 err pos service program lba ubun figure 配置 supervisor [program:HelloWebApp] command=dotnet run directory=/home/python/dotnet/myw

深度學習神經網路的優化方法

前言 \quad\quad 我們都知道,神經網路的學習目的是找到使損失函式的值儘可能小的引數,這是一個尋找最優引數的

深度學習深入理解優化器Optimizer演算法(BGD、SGD、MBGD、Momentum、NAG、Adagrad、Adadelta、RMSprop、Adam)

1.http://doc.okbase.net/guoyaohua/archive/284335.html 2.https://www.cnblogs.com/guoyaohua/p/8780548.html   原文地址(英文論文):https://www.cnblogs.c

機器學習決策樹剪枝優化及視覺化

前言 \quad\quad 前面,我們介紹了分類決策樹的實現,以及用 sklearn 庫中的 DecisionTre

netcore基礎ubuntu 16.04 搭建.net core 2.1 linux 執行環境 nginx反向代理 supervisor配置自啟動 .NetCore學習ubuntu16.04 搭建.net core mvc api 執行環境 .Net Core 部署到Ubuntu 16.0

今天來整理下netcore在linux(ubuntu)上的執行環境搭建   對應版本 ubuntu 16.04 .net core 2.1 nginx version: nginx/1.10.3 (Ubuntu) supervisor Supervisorhttp://super

效能優化App啟動時間

App啟動模式分類 1.冷啟動 冷啟動狀態:系統不存在該應用的程序。啟動應用才能創建出應用的程序。 一般是中應用在開機後或者系統停止後的第一次啟動過程。因為系統和應用在冷啟動時需要做跟多的工作 所以減少

IOS學習到底什麼時候才需要在ObjC的Block中使用weakSelf/strongSelf

Objective C 的 Block 是一個很實用的語法,特別是與GCD結合使用,可以很方便地實現併發、非同步任務。但是,如果使用不當,Block 也會引起一些迴圈引用問題(retain cycle)—— Block 會 retain ‘self’,而 ‘self‘

Ionic實戰一個和AngularJS的跨平臺(iOS,Android) APP框架

關於 使用HTML5和CSS來開發手機應用,一直是廣大前端開發者的理想,並且已經有不少解決方案了。例如 PhoneGap(用javascript來呼叫裝置原生API)JQuery Mobile(UI庫)Titanium(混合方式)AppCan(國產的開發工具) Ioni

機器學習TensorFlow 在 iOS 端的用例

機器學習這種計算方式,於上世紀就已經被世人所知,但是受限制於計算機的計算能力和網路速度等原因,沒有得到發展。在摩爾效應下,現在的計算機效能大幅提升,即便是手上的iPhone,都會比當時美國登月所使用的機器要強。於是,在這個背景下,機器學習開始飛速發展,各大公

java學習9.時間的概念

十、時間的概念 //1.時間本質 Date now=new Date();//獲得當前的CMOS時間 System.out.println(now); //時間本質上是一個長整數,是1970-1-1(GMT)到現在所經過的毫秒數 System.out.println(now

C++學習之如何用系統日期時間給檔案命名

參考的網上的模板,直接給出程式: #include "ctime" #include "time.h" using namespace std; string int2string(int value) { stringstream ss;

iOS學習Macbook外接2k顯示器開啟hidpi的方法

一、前言:大家平時用macbook開發的時候一般都喜歡外接一個顯示器開發吧?這裡我用了一臺2k的顯示器,我們要開啟hidpi模式。你們會問到為什麼要開啟hidpi模式呢?我的2k顯示器是2560*1440分別率,如果採用預設的設定,那麼在顯示器上面的字會特別的小。如果我們開啟

機器學習梯度下降演算法及梯度優化演算法

用於評估機器學習模型的就是損失函式,我們訓練的目的基本上都是最小化損失,這個最小化的方式就要用優化演算法了,機器學習中最常用的就是梯度下降演算法。 導數、方向導數和梯度 要了解梯度下降演算法是什麼首要知道梯度是什麼,導數和方向導數又是瞭解梯度的前提。

優化app啟動頁 解決黑屏時間長的問題

要解決這個問題其實挺簡單的只需要一個樣式style即可1、在style.xml中新增下面的style  設定啟動圖片<style name="SplashTheme" parent="Theme.AppCompat.Light.NoActionBar">     

iOS開發launch Images啟動圖片設定(UILaunchImageFile)之002

一、實現效果: 通過直接給圖片起預設的名字,讓app啟動頁自動載入啟動圖片。 二、注意點: ·圖片命名一定要按蘋果官方的指定規則命名,圖片的畫素也要符合規則; ·如果橫豎屏圖片都需要,記得在Xcode中勾選上專案支援橫豎屏。 三、官方描述:

IOS學習面試iOS工程師的相關問題

1. OC中,與alloc語義相反的方法是dealloc還是release?與retain語義相反的方法是dealloc還是release?為什麼?需要與alloc配對使用的方法是dealloc還是release,為什麼? 以下是針對MRC(手動記憶體釋放)模式: 與all