(一)卡頓優(yōu)化
【了解CPU和GPU】
在屏幕成像過程中,CPU和GPU的作用是至關(guān)重要的翔悠。
- CPU - Central Processing Unit业崖,中央處理器野芒,在iOS程序中,負責對象的創(chuàng)建和銷毀双炕、對象屬性的調(diào)整狞悲、布局的計算、文本的計算和排版規(guī)格妇斤、圖片的格式轉(zhuǎn)碼和解碼摇锋、圖像的繪制(Core Graphic)
-
GPU - Graphics Processing Unit,圖形處理器站超,負責紋理的渲染荸恕。如果沒有接觸過OpenGL的朋友,可能不太好理解紋理渲染這個概念死相,我們知道戚炫,屏幕上面的物理元件是像素,我們在屏幕上面看到的圖片媳纬,文字双肤,視頻,就是由屏幕上的所有像素钮惠,通過控制色值變化而呈現(xiàn)出來的茅糜。那么像素的色值數(shù)據(jù),就是由GPU計算得出的素挽,然后將這些數(shù)據(jù)提交給視頻控制器蔑赘,由它負責顯示到屏幕上。
iOS中采用的是雙緩沖機制预明,分為前幀緩存和后幀緩存缩赛。
【屏幕成像原理】
屏幕成像原理是一個非常龐大的知識體系,這里僅介紹一下我們當前所需要了解的部分撰糠,以便我們接下來的話題色查。
屏幕的顯示昔馋,是受控于兩種信號
-
垂直同步信號(VSync)
屏幕發(fā)出VSync之后,就表示將要進行新一幀畫面的顯示,于是開始從幀緩存里面讀取經(jīng)過GPU渲染好的用于顯示的數(shù)據(jù)
-
水平同步信號(HSync)
顯示器從幀緩存里拿到數(shù)據(jù)之后璃氢,是從上到下一行一行的刷新的透葛,刷新完一行贷揽,就發(fā)出一個HSync拆撼,直到最下面一層顯示出來,這樣辉词,一幀的畫面就完成了顯示必孤。
我們可以把屏幕想象成刷墻師傅,每一幀的數(shù)據(jù)就是桶里的油漆瑞躺,而GPU就是負責提供油漆的店老板敷搪。
【卡頓產(chǎn)生的原因】
我們手機屏幕的刷幀率是60FPS(Frame per Second 幀/秒)兴想,也就是會所1秒鐘的時間,屏幕可以刷新60幀(次)购啄。完成一幀刷新的用時是16.6毫秒襟企。因此垂直同步信號VSync就是每16.6毫秒發(fā)出一次嘱么。
兩次VSync之間的這16.6毫秒狮含,就是被CPU和GPU共同完成下一幀畫面的計算和渲染工作的時間。但是CPU計算和GPU渲染所用的時間是取決于任務(wù)的運算量的曼振,因此就有可能大于16.6毫秒几迄,也有可能小于或者等于16.6毫秒。
這里我們假設(shè)Tc
=CPU計算時間冰评,Tg
=GPU渲染時間映胁。如果Tc
+Tg
<= 16.6ms
,那么完美甲雅,下一幀畫面的數(shù)據(jù)可以在VSync到來之間就準備好解孙;但是如果Tc
+Tg
> 16.6ms
,意味著屏幕將要開始顯示下一幀畫面了抛人,但是CPU和GPU那里卻還在咔咔咔的準備著畫面數(shù)據(jù)弛姜,那么沒辦法,在接下來的16.6ms周期里面妖枚,屏幕就繼續(xù)用上一幀的畫面數(shù)據(jù)來顯示廷臼。同一個畫面被顯示了過長的時間,就造成了視覺上可感知到的卡頓現(xiàn)象绝页。再通過下圖來體會一下
【卡頓優(yōu)化-CPU】
上看我們了解了產(chǎn)生的原因荠商,就是由于CPU計算時間和GPU的渲染時間過長導致的。因此想要優(yōu)化卡頓問題续誉,無非就是從CPU和GPU下手莱没,減輕它們的工作量,以控制它們的操作耗時酷鸦。
首先開看看CPU郊愧,我們有如下途徑來減輕它的計算任務(wù)
盡量用輕量級的對象,比如簡單的數(shù)字井佑,盡量選擇基礎(chǔ)數(shù)據(jù)類型属铁,不要使用
NSNumber
,對象操作的開銷肯定大于基礎(chǔ)數(shù)據(jù)類型的開銷躬翁。
CALayer
是用來顯示圖像的焦蘑,UIView
是負責處理觸摸交互事件的,UIView
內(nèi)部封裝了CALayer
屬性盒发,因此UIView
的圖像顯示實際上是它內(nèi)部的這個CALayer
來完成的例嘱。因此如果我們不需要考慮觸摸事件狡逢,只是單純的要顯示內(nèi)容的話,可以考慮用CALayer
取代UIView
拼卵。
盡量提前計算好布局奢浑,在有需要的時候一次性調(diào)整對應屬性,不要多次修改
圖片的size最好是跟
UIImageView
的size
剛好一致腋腮,可以省去圖片剪裁的操作開銷
控制一下線程的最大并發(fā)數(shù)
盡量把耗時操作放到子線程處理(比如文本的尺寸計算雀彼、繪制,圖片的解碼即寡、繪制)
【卡頓優(yōu)化-GPU】
對與GPU徊哑,有下列方案可以減少渲染開銷
- 盡量避免段時間內(nèi)大量的圖片顯示,盡可能將多張圖片合成一張圖片顯示聪富,比如說三張圖片同時顯示莺丑,不如將這三張圖片合成到一塊作為一張圖片來顯示
- GPU能處理的最大紋理尺寸是
4096*4096
,一旦超過這個尺寸墩蔓,就會占用CPU資源進行處理梢莽,這樣勢必影響CPU的運算效率,因此紋理盡量不要超過這個尺寸 - 盡量減少視圖的數(shù)量和層級
- 減少不必要的透明的視圖
- 盡量避免離屏渲染
【離屏渲染】
在OpenGL中奸披,GPU有兩種渲染方式
- On-Screen Rendering:當前屏幕渲染昏名,在當前用于顯示的屏幕緩沖區(qū)進行渲染操作
- Off-Screen Rendering:離屏渲染,在當前屏幕緩沖區(qū)以外開辟一個新的緩沖區(qū)進行渲染操作
為什么離屏渲染消耗性能源内?
- 需要創(chuàng)建新的緩沖區(qū)
- 離屏渲染的整個過程中葡粒,需要多次切換上下文環(huán)境,先是從當前屏幕緩沖區(qū)(On-Screen)切換到離屏緩沖區(qū)(Off-Screen)膜钓,等完成離屏渲染操作之后嗽交,將離屏緩沖區(qū)的渲染結(jié)果顯示到屏幕上,然后還需要將上下文環(huán)境從離屏緩沖區(qū)切換回當前屏幕緩沖區(qū)颂斜。
哪些操作會觸發(fā)離屏渲染夫壁?
- 光柵化操作
layer.shouldRasterize = YES
- 遮罩設(shè)置
layer.mask
- 圓角設(shè)置
layer.masksToBounds = YES
&layer.cornerRadius>0
??可以考慮通過CoreGraphics繪制剪裁圓角,或者叫美工提供圓角圖片?? - 陰影設(shè)置
layer.shadowXXX
??如果設(shè)置了layer.shadowPath就不會產(chǎn)生離屏渲染??
【卡頓檢測】
平時我們所碰到的卡頓沃疮,主要是在主線程執(zhí)行了比較耗時的操作盒让,可以在主線程RunLoop中添加observer
,通過監(jiān)聽RunLoop的狀態(tài)切換耗時司蔬,來監(jiān)控卡頓邑茄。
(二)耗電優(yōu)化
iOS 設(shè)備耗電的主要來源有一下幾種- CPU處理 Processing
- 網(wǎng)絡(luò) Networking
- 定位 Location
- 圖像 Graphics
耗電優(yōu)化方案
- 盡可能降低CPU、GPU功耗
- 少用定時器
- 優(yōu)化I/O操作
- 盡量不要頻繁的進行數(shù)據(jù)寫入操作俊啼,最好批量一次性寫入
- 讀寫大量重要數(shù)據(jù)時肺缕,考慮用
dispatch_io
,它提供了基于GCD的異步操作文件I/O的API。使用它時同木,系統(tǒng)會優(yōu)化磁盤的訪問 - 數(shù)據(jù)量比較大的話浮梢,建議使用數(shù)據(jù)庫
- 網(wǎng)絡(luò)優(yōu)化
- 減少、壓縮網(wǎng)絡(luò)數(shù)據(jù)
- 如果多次請求的結(jié)果是相同的彤路,盡量使用緩存
- 使用斷點續(xù)傳秕硝,否則網(wǎng)絡(luò)不穩(wěn)定時可能會導致多次傳輸相同內(nèi)容
- 網(wǎng)絡(luò)不可用時,不要嘗試執(zhí)行網(wǎng)絡(luò)請求
- 讓用戶取消長時間運行或者速度很慢的網(wǎng)絡(luò)操作洲尊,設(shè)置合適的超時時間
- 批量傳輸远豺,比如,下載視頻流是颊郎,不要傳輸很小的數(shù)據(jù)包憋飞,直接下載整個文件或者一大塊一大塊下載霎苗。如果下載廣告姆吭,一次性多下載一些,然后慢慢的拿出來展示唁盏。如果狹隘電子郵件内狸,就一次下載多封,不要一封一封下載
- 定位優(yōu)化
- 如果只是需要確定用戶的位置厘擂,最好用
CLLocationManager
的requestLocation
方法昆淡。定位完成后,會自動讓定位硬件斷電刽严。 - 如果不是導航引用昂灵,盡量不要實時更新位置,定位完畢就關(guān)閉掉定位服務(wù)
- 需要后臺定位時舞萄,盡量設(shè)置
pauseLocationUpdateAutomatically
為YES
眨补,這樣,如果用戶不太可能移動的時候倒脓,系統(tǒng)就會自動暫停位置更新撑螺。 - 盡量不要使用
startMonitoringSignificantLocationChanges
,優(yōu)先考慮startMonitoringForRegion:
- 如果只是需要確定用戶的位置厘擂,最好用
- 硬件檢測優(yōu)化:
用戶移動崎弃、搖晃甘晤、傾斜設(shè)備時,會產(chǎn)生動作事件(motion)饲做,這些事件是由加速度計线婚、陀螺儀、磁力傳感器等硬件檢測的盆均。在不需要檢測的場合塞弊,應該及時關(guān)閉這些硬件。
(三)啟動優(yōu)化
【App啟動過程分析】
App的啟動可以分為兩種
- 冷啟動:從零開始啟動App
- 熱啟動:App已經(jīng)在內(nèi)存中,處在后臺狀態(tài)中居砖,在次點擊圖標啟動App
App的啟動時間優(yōu)化虹脯,主要是針對冷啟動來進行優(yōu)化的。那么首先我們就需要了解一下App的冷啟動過程包含哪些步驟奏候。
- (一)
dyld
階段:dyld1dynamic link editor
)循集,Apple的動態(tài)鏈接器,可用來加載Mach-O
文件(可執(zhí)行文件蔗草、動態(tài)庫等等)咒彤。冷啟動一個app之后,首先是dyld
開始工作咒精,它負責兩件事情:- 加載App的可執(zhí)行文件镶柱,同時會遞歸加載所有依賴庫的動態(tài)庫。
- 當完成可執(zhí)行文件和動態(tài)庫的加載之后模叙,就通知
Runtime
進行下一步處理歇拆。
- (二)
Runtime
階段:在這個階段,Runtime
做了如下的工作:- 調(diào)用
map_images
函數(shù)對可執(zhí)行文件的內(nèi)容進行解析和處理范咨。 - 在
load_images
函數(shù)中調(diào)用call_load_methods
故觅,以調(diào)用所有Class和Category的+load
方法。 - 進行各種objc結(jié)構(gòu)的初始化(例如objc類的注冊渠啊,初始化類對象等等)
- 調(diào)用C++靜態(tài)初始化器以及被
_attribute_((constructor))
修飾的函數(shù)
- 調(diào)用
到此為止输吏,可執(zhí)行文件和動態(tài)庫中的所有所有Symbols(符號,包括 Class替蛉, Protocol贯溅, Selector, IMP等等)都已經(jīng)按照規(guī)定格式加載到內(nèi)存中躲查,并且被runtime所管理
- (三)
main
函數(shù)階段
小結(jié) - App的冷啟動由
dyld
主導它浅,將可執(zhí)行文件加載到內(nèi)存,順便把所依賴的動態(tài)庫也加載到內(nèi)存熙含,然后通知runtime
進行相應處理 -
runtime
負責將上面的內(nèi)容初始化成`bjc定義的結(jié)構(gòu)體 - 最后當初始化工作完成后罚缕,
dyld
就會調(diào)用main
函數(shù)。接下來便是main函數(shù)
-->UIApplicationMain函數(shù)
-->didFinishLaunchingWithOptions方法
怎静。
【App啟動優(yōu)化】
dyld階段優(yōu)化
- 減少動態(tài)庫邮弹,合并一些動態(tài)庫,定期清理不必要的動態(tài)庫
- 減少Objc類蚓聘、
Category
的數(shù)量腌乡,減少Selector
數(shù)量,定期清理不再需要的Class
和Category
- 減少C++虛函數(shù)的數(shù)量夜牡,因為虛函數(shù)會導致額外的虛表的存在
- 如果是Swift盡量使用
struct
Runtime階段優(yōu)化
- 用
+initialize方法
+dispatch_once
的組合來取代所有的_attribute_((constructor))
与纽、C++靜態(tài)構(gòu)造器侣签、Objc的+load
方法。
main階段優(yōu)化
在不影響用戶體驗的前提下急迂,盡可能講一些操作延遲影所,不要全都放在didFinishLaunchingWithOptions
方法中。
(四)安裝包瘦身
iOS的安裝包(ipa)主要有可執(zhí)行文件和資源組成
可執(zhí)行文件——就是由我們iOS項目的代碼經(jīng)過編譯連接產(chǎn)生的二進制文件僚碎,要想對可執(zhí)行文件進行瘦身猴娩,有如下幾種思路:
- 編譯器優(yōu)化
Strip Linked Product
、Make Strings Read-Only
勺阐、Symbols Hidden by Default
設(shè)置為YES
去掉異常支持卷中,Enable C++ Exceptions
、Enable Objective-C Exceptions
設(shè)置為NO
渊抽,Other C Flags
添加-fno-exceptions
- 通過第三方工具AppCode檢測項目中用不到的代碼:菜單 --> Code --> Inspect Code
資源——包括我們iOS項目中的圖片蟆豫、音頻、視頻懒闷、storyboard等十减,對其進行優(yōu)化的思路有 - 對資源進行無損壓縮
- 去除沒有用到的資源(第三方檢測工具)