來自:slimsallen
連結:https://juejin.im/post/5b7a9c466fb9a019eb43b0d5
好的app應該有好的效能流暢度,本篇文章就大概講一下ios效能最佳化。
先來談談CPU和GPU
在螢幕成像的過程中,CPU和GPU起著至關重要的作用
CPU( Central Processing Unit, 中央處理器)就是機器的“大腦”,也是佈局謀略、發號施令、控制行動的“總司令官”。
CPU的結構主要包括運算器(ALU, Arithmetic and Logic Unit)、控制單元(CU, Control Unit)、暫存器(Register)、高速快取器(Cache)和它們之間通 訊的資料、控制及狀態的匯流排。
GPU全稱為Graphics Processing Unit,中文為圖形處理器,就如它的名字一樣,GPU最初是用在個人電腦、工作站、遊戲機和一些移動裝置(如平板電腦、智慧手機等)上執行繪圖運算工作的微處理器。
為什麼GPU特別擅長處理影象資料呢?這是因為影象上的每一個畫素點都有被處理的需要,而且每個畫素點處理的過程和方式都十分相似,也就成了GPU的天然溫床。
在iOS中是雙緩衝機制,有前幀快取、後幀快取,即GPU會預先渲染好一幀放入一個緩衝區內(前幀快取),讓影片控制器讀取,當下一幀渲染好後,GPU會直接把影片控制器的指標指向第二個緩衝器(後幀快取)。當你影片控制器已經讀完一幀,準備讀下一幀的時候,GPU會等待顯示器的VSync訊號發出後,前幀快取和後幀快取會瞬間切換,後幀快取會變成新的前幀快取,同時舊的前幀快取會變成新的後幀快取。
螢幕成像原理
卡頓產生的原因
在Sync訊號到來後,系統圖形服務會透過CADisplayLink等機制通知App,App主執行緒開始在CPU中計算顯示內容,比如檢視的建立,佈局計算,圖片解碼,文字繪製等。隨後CPU會將計算好的內容提交到GPU去,由GPU進行交換,合成,渲染。隨後GPU會把渲染結果提交到幀緩衝區,等待下一次VSync訊號(垂直同步訊號)到來時顯示到螢幕上。由於垂直同步機制,如果在一個VSync時間內,CPU或者GPU沒有完成內容提交,則那一幀就會被丟棄,等待下一次機會再顯示,而這時顯示屏因為沒有新的掃清,會保留之前的內容不變。這就造成了卡頓。
-
按照60FPS的刷幀率,每隔16ms就會有一次VSync訊號
卡頓最佳化 -CPU
-
儘量用輕量級的物件,比如用不到事件處理的地方,可以考慮使用CALayer取代UIView
-
不要頻繁地呼叫UIView的相關屬性,比如frame、bounds、transform等屬性,儘量減少不必要的修改
-
儘量提前計算好佈局,在有需要時一次性調整對應的屬性,不要多次修改屬性
-
Autolayout會比直接設定frame消耗更多的CPU資源
-
圖片的size最好剛好跟UIImageView的size保持一致
-
控制一下執行緒的最大併發數量
-
儘量把耗時的操作放到子執行緒
卡頓最佳化 -GPU
-
儘量避免短時間內大量圖片的顯示,盡可能將多張圖片合成一張進行顯示
-
儘量減少檢視數量和層次
-
減少透明的檢視(alpha<1),不透明的就設定opaque為YES
-
儘量避免出現離屏渲染
離屏渲染
-
在OpenGL中,GPU有2種渲染方式
1、On-Screen Rendering:當前螢幕渲染,在當前用於顯示的螢幕緩衝區進行渲染操作
2、Off-Screen Rendering:離屏渲染,在當前螢幕緩衝區以外新開闢一個緩衝區進行渲染操作
-
離屏渲染消耗效能的原因
1、需要建立新的緩衝區
2、離屏渲染的整個過程,需要多次切換背景關係環境,先是從當前螢幕(On-Screen)切換到離屏(Off-Screen);等到離屏渲染結束以後,將離屏緩衝區的渲染結果顯示到螢幕上,又需要將背景關係環境從離屏切換到當前螢幕
-
哪些操作會觸發離屏渲染?
1、光柵化,layer.shouldRasterize = YES
2、遮罩,layer.mask
3、圓角,同時設定layer.masksToBounds = YES、layer.cornerRadius大於0。考慮透過CoreGraphics繪製裁剪圓角,或者叫美工提供圓角圖片
4、陰影,layer.shadowXXX。如果設定了layer.shadowPath就不會產生離屏渲染
耗電最佳化
耗電的主要來源?
-
CPU處理,Processing
-
網路,Networking
-
定位,Location
-
影象,Graphics
定位最佳化
-
如果只是需要快速確定使用者位置,最好用CLLocationManager的requestLocation方法。定位完成後,會自動讓定位硬體斷電
-
如果不是導航應用,儘量不要實時更新位置,定位完畢就關掉定位服務
-
儘量降低定位精度,比如儘量不要使用精度最高的kCLLocationAccuracyBest
-
需要後臺定位時,儘量設定pausesLocationUpdatesAutomatically為YES,如果使用者不太可能移動的時候系統會自動暫停位置更新
-
儘量不要使用startMonitoringSignificantLocationChanges,優先考慮startMonitoringForRegion:
APP啟動最佳化
先來看app啟動流程
APP的啟動可以分為2種
1、冷啟動(Cold Launch):從零開始啟動APP
2、熱啟動(Warm Launch):APP已經在記憶體中,在後臺存活著,再次點選圖示啟動APP
APP啟動時間的最佳化,主要是針對冷啟動進行最佳化
-
透過新增環境變數可以打印出APP的啟動時間分析(Edit scheme -> Run -> Arguments)
1、DYLD_PRINT_STATISTICS設定為1
2、如果需要更詳細的資訊,那就將DYLD_PRINT_STATISTICS_DETAILS設定為1
APP的冷啟動概括為三大階段
-
dyld,Apple的動態聯結器,可以用來裝載Mach-O檔案(可執行檔案、動態庫等)
啟動APP時,dyld所做的事情有
1、裝載APP的可執行檔案,同時會遞迴載入所有依賴的動態庫
2、當dyld把可執行檔案、動態庫都裝載完畢後,會通知Runtime進行下一步的處理
-
runtime
啟動APP時,runtime所做的事情有
1、呼叫map_images進行可執行檔案內容的解析和處理
2、在load_images中呼叫call_load_methods,呼叫所有Class和Category的+load方法
3、進行各種objc結構的初始化(註冊Objc類 、初始化類物件等等)
4、呼叫C++靜態初始化器和__attribute__((constructor))修飾的函式
到此為止,可執行檔案和動態庫中所有的符號(Class,Protocol,Selector,IMP,…)都已經按格式成功載入到記憶體中,被runtime 所管理
-
main
1、APP的啟動由dyld主導,將可執行檔案載入到記憶體,順便載入所有依賴的動態庫
2、並由runtime負責載入成objc定義的結構
3、所有初始化工作結束後,dyld就會呼叫main函式
4、接下來就是UIApplicationMain函式,AppDelegate的application:didFinishLaunchingWithOptions:方法
最佳化方案
一、dyld
-
減少動態庫、合併一些動態庫(定期清理不必要的動態庫)
-
減少Objc類、分類的數量、減少Selector數量(定期清理不必要的類、分類)
-
減少C++虛函式數量
-
Swift儘量使用struct
二、runtime
用+initialize方法和dispatch_once取代所有的__attribute__((constructor))、C++靜態建構式、ObjC的+load
三、main
-
在不影響使用者體驗的前提下,盡可能將一些操作延遲,不要全部都放在finishLaunching方法中
-
按需載入
後面會分享自己的最佳化過程
Follow:https://github.com/sallenhandong
Source:https://slimsallen.com/#/detail/iosperformance.md
●編號287,輸入編號直達本文
●輸入m獲取文章目錄
Web開發
更多推薦《18個技術類微信公眾號》
涵蓋:程式人生、演演算法與資料結構、駭客技術與網路安全、大資料技術、前端開發、Java、Python、Web開發、安卓開發、iOS開發、C/C++、.NET、Linux、資料庫、運維等。