iOS開發系列之性能優化

本篇主要記錄一下我對界面優化、時間優化和耗電優化、安裝包瘦身上的一些探索。我儘可能按照本身的理解來進行描述,若有不當,歡迎指正。php

## 1、界面優化html

### 一、卡頓原理ios

要了解卡頓原理,須要對幀緩衝區、垂直同步、CPU 和 GPU 幾個詞進行一下了解,而後綜合起來,就能夠獲得卡頓的答案。git

#### 1.一、幀緩衝區github

聽起來很高大上,其實就是用來存放每一幀畫面數據的一個 「倉庫」,一個倉庫只存放一幀畫面的數據,iOS 一直是雙緩存,就是有兩個倉庫,存當前幀數據的叫 「正式倉庫」,存下一幀數據的叫 「預備倉庫」。緩存

當正式倉庫的數據被取走後,二者身份交換,原來的預備倉庫轉正爲正式倉庫,原來的正式倉庫變成預備倉庫。性能優化

#### 1.二、垂直同步 (VSync)網絡

就是一個「信號」,通知 APP 該開始準備往預備倉庫裏存放數據了,系統過一會就要來取,這個時間大概是 16.7 毫秒。架構

#### 1.三、CPU (中央處理器)app

主要的工做有:正式對象的建立和銷燬、對象屬性的調整、佈局計算、文本的計算和排版、圖片的格式轉換和解碼、圖像的繪製。咱們能夠理解爲負責包裹內部的處理工做,簡稱 「打包」。

#### 1.四、GPU (圖形處理器)

主要的工做有:將 CPU 計算好的內容進行變換、合成、渲染等處理,而後將渲染結果提交到幀緩衝區。咱們能夠理解爲,對 CPU 給過來的包裹進行分類、排列等操做後,存放到倉庫裏去,簡稱 「入庫」。

#### 1.五、卡頓原理

當收到系統發過來的 VSync 「信號」後,CPU 就開始對這一幀畫面的數據進行 「打包」,而後交給 GPU 進行 「入庫」 操做,存入到 「預備倉庫」 中。

16.7 毫秒後,預備倉庫轉正,系統開始讀取倉庫裏的數據,若是這個時候,倉庫中的數據尚未準備好,那麼系統就會大發雷霆,放棄讀取倉庫中的數據。

那麼這個時候,由於系統的寧缺毋濫,致使了顯示器上顯示的仍是上一幀畫面,就形成了卡頓的效果。

### 二、優化

做爲軟件開發工程師的咱們,既不能延長 16.7 毫秒的處理時間,也不能改變系統的脾氣,那咱們能作的就是儘可能在這個時間內完成數據的準備。要麼 「打包」 快一點,要麼 「入庫」 快一點,也就是針對 CPU 和 GPU 的工做進行優化,這就是性能優化的工做了。

#### 2.一、CPU 工做之正式對象的建立和銷燬

- UITableViewCell 和 UICollectionViewCell 的複用,能夠減小 cell 的建立操做;

- 儘可能使用輕量級的對象,能夠減小對象的建立時間,好比在不須要事件處理的場景裏,使用CALayer 比 UIView 會更加合適;

- 表情鍵盤使用 UICollectionViewCell 代替 UIButton,能夠減小對象的建立操做;

- 對象不涉及UI操做,放到後臺線程建立;

- 性能敏感的界面,storyborad 的資源消耗>代碼建立;

- 推遲對象建立的時間,對象放到多個任務中,好比懶加載;

#### 2.二、CPU 工做之對象屬性的調整

- UIView 有一個 CALyer 的屬性,UIView 負責事件的處理,CALyer 負責圖層的繪製和顯示。須要注意的是,CALyer自己是沒有屬性的,因此當改變 UIView 的顯示相關的屬性如 frame、bounds 和 transform 的時候,會消耗較多的資源,因此減小對這些屬性的一些沒必要要修改,能減少 CPU 的壓力;

#### 2.三、CPU 工做之佈局計算

- UITableViewCell 高度提早計算並存儲,要用的時候直接讀取;

- frame 計算好,減小沒必要要的修改;

- Autolayout 會比直接設置 frame 消耗更多的 CPU 資源;

#### 2.四、CPU 工做之文本的計算和排版

- 普通文本能夠在子線程用 [NSAttributedString boundingRectWithSize:options:context:] 來計算文本寬高,用 -[NSAttributedString drawWithRect:options:context:] 來繪製文本;

- CoreText 對象佔用內存較少,當顯示大量文本時,能夠用 CoreText 對文本異步繪製;

#### 2.五、CPU 工做之圖片的格式轉換和解碼

- 在後臺線程先把圖片繪製到 CGBitmapContext 中,而後從 Bitmap 直接建立圖片;

#### 2.六、CPU 工做之圖像的繪製

- UITableViewCell 滑動減速的時候才加載圖片,能夠參考這個[demo](https://github.com/johnil/VVeboTableViewDemo);

- 加載圖片時,imageNamed 方法默認加載圖片成功後會內存中緩存圖片,下次讀取會很快;imageWithContentsOfFile 方法不會緩存圖片,大圖片可使用該方法;

#### 2.七、GPU 工做之渲染

- 儘可能不要讓圖片和視圖的大小超過 GPU 紋理尺寸上限:4096 × 4096,否則圖片還須要通過 CPU 的處理;

- 儘可能減小視圖數量和層次,並設置視圖爲不透明,UIView 的不透明屬性 (opaque) 默認爲 YES,通常設置背景顏色便可;CALayer 的不透明屬性 (opaque) 默認爲 NO,須要設置爲 YES;

- CALayer 的 border、圓角、陰影、遮罩,一般會觸發離屏渲染,儘可能少用,圓角屬性可使用 CoreGraphics 繪製或使用圓角圖片代替,關於離屏渲染的知識,具體能夠參考這篇文章:[iOS 圖形性能優化](http://www.cocoachina.com/cms/wap.php?action=article&id=25543);

- 圖片的 size 最好恰好跟 UIImageView 的 size 保持一致,這涉及到像素對齊的知識,也能夠在上面這篇文章中詳細瞭解;

## 2、時間優化

要談論時間優化,就要先了解程序啓動的過程和耗時的緣由,而後針對性的進行優化。

### 一、程序啓動過程

程序的啓動分爲冷啓動和熱啓動兩種模式,其中冷啓動是從程序被殺死後加載起來的過程,熱啓動是從後臺到前臺的過程。相比之下,熱啓動是包含在冷啓動裏,而且比冷啓動少了部分加載過程的,因此,咱們日常說的啓動優化,通常都是針對冷啓動的。

從點擊程序的圖標,到首頁渲染完成顯示到用戶眼前,主要有三個階段。

#### 1.一、階段一:main 函數以前

該階段主要進行動態連接庫 (dylib) 和自身 App 可執行文件的加載。

其中動態連接庫包括:iOS 中用到的全部系統 framework,加載 OC runtime 方法的 libobjc,系統級別的 libSystem,例如 libdispatch(GCD) 和 libsystem_blocks (Block)。

#### 1.二、階段二:main 函數到首頁加載以前

該階段主要執行 main 函數到 applicationWillFinishLaunching 方法結束。

#### 1.三、階段三:首頁開始加載到渲染完成

該階段主要執行首頁界面 viewDidLoad 方法和 UITabBarController 第一個子控制器 viewWillAppear 裏的代碼。

### 二、耗時產生緣由

#### 2.一、階段一里可能產生耗時的有:

- 加載大量動態連接庫;

- 註冊大量 Objc 類 、初始化類對象 (Objc 的 +load 方法);

- 加載大量分類裏的方法;

- 加載大量 C++ 靜態對象;

- 執行大量聲明爲 __attribute__((constructor)) 的C函數。

#### 2.二、階段二里可能產生耗時的有:

- 在 applicationWillFinishLaunching 執行了 UITabBarController 以及 子控制器的建立,並在 viewDidLoad 方法裏執行了大量的耗時操做;

- 大量第三方應用的配置和啓動項的累積;

#### 2.三、階段三裏可能產生耗時的有:

- 在 UITabBarController 第一個子控制器的 viewWillAppear 方法裏執行了大量的耗時操做;

### 三、啓動時間優化

#### 3.一、針對階段一的優化:

- 減小非系統庫的依賴、合併不是系統庫,蘋果最多支持6個非系統的動態庫合併爲一個;

- 按期清理項目裏不使用的類和方法,檢測工具可使用AppCode,關於AppCode的使用請參考這篇文章: [AppCode使用介紹](https://www.jianshu.com/p/6be7dc65d67e);

- 將沒必要須在 +load 方法中作的事情延遲到 +initialize 中,關於兩者的區別能夠參考[這篇文章](https://ityongzhen.github.io/iOS中load和initialize.html);

- 減小分類和分類裏方法的數量;

- 儘可能不要用 C++ 虛函數;

- 刪減一些無用的靜態變量。

#### 3.二、針對階段二的優化:

- 對啓動項和第三方應用配置根據優先級區分,部分不須要在程序啓動就初始化的配置,進行延後處理;

- 減小在 viewDidLoad 裏的耗時處理;

#### 3.三、針對階段三的優化:

- UITabBarController 第一個子控制器裏的一些耗時操做能夠放到 viewDidAppear 方法中,先把界面加載出來,而後再拿到數據刷新界面;

- 增長廣告頁,在這個時間內準備好首屏頁面和數據;

### 四、耗時檢測工具

- Xcode自帶的 Time Profiler,具體操做能夠參考[這篇文章](https://blog.csdn.net/lichuandev/article/details/79636363);

- 查詢main函數以前的耗時:菜單:Product->Scheme->Edit Scheme->Environment Variables,設置:key:DYLD_PRINT_STATISTICS ,value:1。

## 3、耗電優化

要研究耗電優化,就要先明白耗電產生的緣由,而後針對性的作出一些優化。根據耗電的緣由和可優化類型,能夠分爲 CPU 和 GPU 操做優化、網絡優化、定位優化、動做傳感器優化和藍牙優化五大類。

### 一、CPU 和 GPU 操做優化

CPU 和 GPU 消耗是全部開發者繞不開的難關,良好的開發習慣,能讓咱們減小不少能耗,這裏能夠將前面介紹的界面優化結合起來,除此以外,有幾個地方能夠進行優化:

- 少用定時器,每次用的時候都仔細思考下:首先,能不能用其餘方式代替,好比定時刷新數據改成觸發式刷新,還有[dispatch source代替定時器監測文件變化](https://www.jianshu.com/p/ccde42d839b8);若是不能代替,那麼注意的是要設置一個合適的超時時間,以及在再也不須要時及時關閉重複性定時器。

- 文件的讀寫操做很耗電,咱們要儘可能減少讀寫操做的頻率,思路有:
小數據和小改動最好批量一次性寫入;使用 SQLite 或 Core Data 存儲大量的數據;

### 二、網絡優化

- 使用斷點續傳,不然網絡不穩定時可能屢次傳輸相同的內容

- 減小、壓縮網絡數據

- 讓用戶能夠取消長時間運行或者速度很慢的網絡操做,設置合適的超時時間

- 網絡不可用時,不要嘗試執行網絡請求

- 批量傳輸,好比,下載視頻流時,不要傳輸很小的數據包,直接下載整個文件或者一大塊一大塊地下載。若是下載廣告,一 次性多下載一些,而後再慢慢展現。若是下載電子郵件,一次下載多封,不要一封一封地下載

### 三、定位優化

- 不少 APP 爲了記錄用戶的活動或者提供基於位置的服務會進行定位。定位精度越高,定位時間越長,消耗電量也就越多。因此 APP 應該儘可能下降定位精度、縮短定位時間。不須要位置信息以後當即中止定位。

- 若是隻是須要快速肯定用戶位置,最好用 CLLocationManager的requestLocation 方法。定位完成後,會自動讓定位硬件斷電

- 若是不是導航應用,儘可能不要實時更新位置,定位完畢就關掉定位服務

- 儘可能下降定位精度,好比儘可能不要使用精度最高的 kCLLocationAccuracyBest

- 須要後臺定位時,儘可能設置 pausesLocationUpdatesAutomatically 爲 YES,若是用戶不太可能移動的時候系統會自動暫停位置更新

- 儘可能不要使用 startMonitoringSignificantLocationChanges,優先考慮 startMonitoringForRegion

### 三、動做傳感器優化

- 用戶移動、搖晃、傾斜設備時,會產生動做(motion)事件,這些事件由加速度計、陀螺儀、磁力計等硬件檢測。在不須要檢測的場合,應該及時關閉這些硬件

- 長時間用不上加速度計、陀螺儀、磁力計等設備的動做數據時,應該中止更新數據,否則也會浪費電能。應按需獲取,用完即停。

### 四、藍牙優化

- 沒有必要的時候不要掃描藍牙外設。

- 掃描外設時通常不要用 CBCentralManagerScanOptionAllowDuplicatesKey。

- 只查找你須要的外設服務。外設可能提供不少服務和特性(characteristic),查找外設的時候能夠指定 UUID。

- 不要輪詢設備特性值,用通知監測特徵值的變化。

- 特性值再也不提供通知或者再也不須要通訊的時候就斷開鏈接。

### 五、耗電檢測

- 在Xcode中選擇View > Navigators > Show Debug Navigator,這裏提供了不少儀表用於分析功耗。Energy impact能夠查看正在運行的app的功耗

- 啓動 Instruments,選擇你的設備和要檢測的 APP,打開 Energy Log 進行檢測

- 設備上進入設置 > 開發者 > Logging > 開啓功耗記錄。注意:若是手機裏沒有開發者選項請看[這裏](https://www.jianshu.com/p/a1d075b3472c)

## 4、安裝包瘦身

安裝包的大小受資源文件和可執行文件影響,因此針對性的優化也是這兩方面。

### 一、資源文件優化

- 在項目中引入圖片時候,直接在 Assets.xcassets 中添加就能夠,這樣能使用到 App Slicing 功能,這樣當用戶從 App Store上下載 App 時,能夠只下載適用於其設備的 App 架構版本和所需資源,從而減小App所佔的空間,且如今基本沒有 1x 屏幕的設備了,因此能夠不用提供這個分辨率的圖片。

- 使用 [LSUnusedResources](https://yq.aliyun.com/go/articleRenderRedirect?url=https%3A%2F%2Fgithub.com%2Ftinymind%2FLSUnusedResources%2F) 查找無用圖片

- 使用 [TinyPNG](https://yq.aliyun.com/go/articleRenderRedirect?url=https%3A%2F%2Ftinypng.com%2F)有 損壓縮圖片

- 啓動圖片通常較大,能夠用 LaunchScreen.storyboard 替換

- 音頻文件、視頻文件和 H5 遠端化

### 二、可執行文件優化

- 清理無用代碼--[AppCode](https://www.jetbrains.com/objc/),用它的 inspect code 來掃描無用代碼,包括無用的類、函數、宏定義、value、屬性等

- 去掉異常支持,Enable C++ Exceptions、Enable Objective-C Exceptions 設置爲 NO, Other C Flags 添加 -fno-exceptions

- 在 release 狀態下,Strip Debug Symbols During Copy、Strip Linked Product、Make String Read-Only、Dead Code Stripping、Deployment PostProcessing、Symbols hidden by default 設爲Y ES,Optimization Level 設置爲 Fastest、Smallest

- OC項目中使用 Swift,會增長安裝包大小,由於 FrameWork 中會加入爲了支持 Swift 的動態庫集合,若是純 Swift 項目,不會引入這些東西。

- 刪除不使用的三方庫,功能用的少可是體積大的三方庫能夠考慮本身重寫,合併功能重複的三方庫

最後鄭重聲明,本篇裏只是記錄一下個人我的總結,資料大量參考瞭如下文章:
[iOS 保持界面流暢的技巧](https://blog.ibireme.com/2015/11/12/smooth_user_interfaces_for_ios/)
[今日頭條iOS客戶端啓動速度優化](https://techblog.toutiao.com/2017/01/17/iosspeed/#more);
[iOS App 啓動性能優化](https://mp.weixin.qq.com/s/Kf3EbDIUuf0aWVT-UCEmbA);
[iOS App冷啓動治理:來自美團外賣的實踐](https://mp.weixin.qq.com/s/jN3jaNrvXczZoYIRCWZs7w);
[iOS app啓動速度研究實踐](https://zhuanlan.zhihu.com/p/38183046?from=1086193010&wm=3333_2001&weiboauthoruid=1690182120);
[iOS進階--App功耗優化看這篇就夠了](http://www.cocoachina.com/articles/21428);
[iOS的性能優化](https://www.jianshu.com/p/49cecc06e99f);
[iOS安裝包瘦身小記](https://www.codercto.com/a/69108.html);
[iOS App 安裝包瘦身指南](https://yq.aliyun.com/articles/226555?utm_content=m_33409)

歡迎你們來[個人小窩](https://zmfflying.github.io/)作客啊,裏面記錄下了我進步的點點滴滴,一切逆境只是前進的理由,與君共勉。

相關文章
相關標籤/搜索