iOS底層原理之性能優(yōu)化

文章目錄
CPU和GPU

卡頓優(yōu)化 - CPU

卡頓優(yōu)化 - GPU

離屏渲染

耗電
耗電優(yōu)化

APP的啟動(dòng)

APP啟動(dòng)3大階段

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

安裝包瘦身

面試題
CPU和GPU

在屏幕成像的過(guò)程中,CPU和GPU起著至關(guān)重要的作用。

CPU(Central Processing Unit,中央處理器):主要用于對(duì)象的創(chuàng)建和銷(xiāo)毀、對(duì)象屬性的調(diào)整、布局計(jì)算、文本的計(jì)算和排版、圖片的格式轉(zhuǎn)換和解碼、圖像的繪制(Core Graphics);

GPU(Graphics Processing Unit,圖形處理器): 主要用于紋理的渲染。

屏幕成像原理

垂直同步信號(hào)(VSync)表示將要顯示一幀畫(huà)面(一屏幕);

水平同步信號(hào)(HSync)表示一行一行顯示,最后一行執(zhí)行完又會(huì)發(fā)送一個(gè)垂直同步信號(hào)(VSync)顯示下一幀畫(huà)面。

卡頓的原因:
因?yàn)榇怪蓖叫盘?hào)(VSync)是按固定的頻率發(fā)出的,發(fā)出后就要顯示當(dāng)前的畫(huà)面,如果cpu和gpu消耗時(shí)間太長(zhǎng),這時(shí)候就只能顯示上一幀畫(huà)面,這樣就會(huì)造成丟幀的現(xiàn)象。

卡頓解決的主要思路

盡可能減少CPU、GPU資源消耗,按照60FPS的刷幀率,每隔16ms就會(huì)有一次VSync信號(hào)。

卡頓優(yōu)化 - CPU

盡量用輕量級(jí)的對(duì)象,比如用不到事件處理的地方,可以考慮使用CALayer取代UIView;

不要頻繁地調(diào)用UIView的相關(guān)屬性,比如frame、bounds、transform等屬性,盡量減少不必要的修改;

盡量提前計(jì)算好布局,在有需要時(shí)一次性調(diào)整對(duì)應(yīng)的屬性,不要多次修改屬性;

Autolayout會(huì)比直接設(shè)置frame消耗更多的CPU資源;

圖片的size最好剛好跟UIImageView的size保持一致;

控制一下線(xiàn)程的最大并發(fā)數(shù)量;

盡量把耗時(shí)的操作放到子線(xiàn)程,如文本處理(尺寸計(jì)算、繪制)、圖片處理(解碼、繪制)。

卡頓優(yōu)化 - GPU
盡量避免短時(shí)間內(nèi)大量圖片的顯示,盡可能將多張圖片合成一張進(jìn)行顯示;

GPU能處理的最大紋理尺寸是4096x4096,一旦超過(guò)這個(gè)尺寸,就會(huì)占用CPU資源進(jìn)行處理,所以紋理盡量不要超過(guò)這個(gè)尺寸;

盡量減少視圖數(shù)量和層次;

減少透明的視圖(alpha<1),不透明的就設(shè)置opaque為YES;

盡量避免出現(xiàn)離屏渲染 。

離屏渲染
在OpenGL中,GPU有2種渲染方式:

On-Screen Rendering:當(dāng)前屏幕渲染,在當(dāng)前用于顯示的屏幕緩沖區(qū)進(jìn)行渲染操作;

Off-Screen Rendering:離屏渲染,在當(dāng)前屏幕緩沖區(qū)以外新開(kāi)辟一個(gè)緩沖區(qū)進(jìn)行渲染操作。

離屏渲染消耗性能的原因:

需要?jiǎng)?chuàng)建新的緩沖區(qū),離屏渲染的整個(gè)過(guò)程,需要多次切換上下文環(huán)境,先是從當(dāng)前屏幕(On-Screen)切換到離屏(Off-Screen);等到離屏渲染結(jié)束以后,將離屏緩沖區(qū)的渲染結(jié)果顯示到屏幕上,又需要將上下文環(huán)境從離屏切換到當(dāng)前屏幕。

卡頓檢測(cè)
平時(shí)所說(shuō)的“卡頓”主要是因?yàn)樵谥骶€(xiàn)程執(zhí)行了比較耗時(shí)的操作,可以添加Observer到主線(xiàn)程RunLoop中,通過(guò)監(jiān)聽(tīng)RunLoop狀態(tài)切換的耗時(shí),以達(dá)到監(jiān)控卡頓的目的。github上有一個(gè)第三方庫(kù)LXDAppFluecyMonitor可以檢測(cè)每個(gè)執(zhí)行方法消耗的時(shí)常。

耗電

耗電的主要來(lái)源:CPU處理,Processing、網(wǎng)絡(luò),Networking、定位,Location、圖像,Graphics。

耗電優(yōu)化

盡可能降低CPU、GPU功耗

少用定時(shí)器

優(yōu)化I/O操作

盡量不要頻繁寫(xiě)入小數(shù)據(jù),最好批量一次性寫(xiě)入讀寫(xiě)大量重要數(shù)據(jù)時(shí),考慮用dispatch_io,其提供了基于GCD的異步操作文件I/O的API。用dispatch_io系統(tǒng)會(huì)優(yōu)化磁盤(pán)訪(fǎng)問(wèn),數(shù)據(jù)量比較大的,建議使用數(shù)據(jù)庫(kù)(比如SQLite、CoreData)。

網(wǎng)絡(luò)優(yōu)化
減少、壓縮網(wǎng)絡(luò)數(shù)據(jù);

如果多次請(qǐng)求的結(jié)果是相同的,盡量使用緩存;

使用斷點(diǎn)續(xù)傳,否則網(wǎng)絡(luò)不穩(wěn)定時(shí)可能多次傳輸相同的內(nèi)容;

網(wǎng)絡(luò)不可用時(shí),不要嘗試執(zhí)行網(wǎng)絡(luò)請(qǐng)求,讓用戶(hù)可以取消長(zhǎng)時(shí)間運(yùn)行或者速度很慢的網(wǎng)絡(luò)操作,設(shè)置合適的超時(shí)時(shí)間批量傳輸,比如,下載視頻流時(shí),不要傳輸很小的數(shù)據(jù)包,直接下載整個(gè)文件或者一大塊一大塊地下載。如果下載廣告,一次性多下載一些,然后再慢慢展示。如果下載電子郵件,一次下載多封,不要一封一封地下載。

定位優(yōu)化
如果只是需要快速確定用戶(hù)位置,最好用CLLocationManager的requestLocation方法。定位完成后,會(huì)自動(dòng)讓定位硬件斷電;

如果不是導(dǎo)航應(yīng)用,盡量不要實(shí)時(shí)更新位置,定位完畢就關(guān)掉定位服務(wù);

盡量降低定位精度,比如盡量不要使用精度最高的kCLLocationAccuracyBest;

需要后臺(tái)定位時(shí),盡量設(shè)置pausesLocationUpdatesAutomatically為YES,如果用戶(hù)不太可能移動(dòng)的時(shí)候系統(tǒng)會(huì)自動(dòng)暫停位置更新;

盡量不要使用startMonitoringSignificantLocationChanges,優(yōu)先考慮startMonitoringForRegion:。

硬件檢測(cè)優(yōu)化
用戶(hù)移動(dòng)、搖晃、傾斜設(shè)備時(shí),會(huì)產(chǎn)生動(dòng)作(motion)事件,這些事件由加速度計(jì)、陀螺儀、磁力計(jì)等硬件檢測(cè)。在不需要檢測(cè)的場(chǎng)合,應(yīng)該及時(shí)關(guān)閉這些硬件。

APP的啟動(dòng)

APP的啟動(dòng)可以分為2種

冷啟動(dòng)(Cold Launch):從零開(kāi)始啟動(dòng)APP;

熱啟動(dòng)(Warm Launch):APP已經(jīng)在內(nèi)存中,在后臺(tái)存活著,再次點(diǎn)擊圖標(biāo)啟動(dòng)APP。

APP啟動(dòng)時(shí)間的優(yōu)化,主要是針對(duì)冷啟動(dòng)進(jìn)行優(yōu)化

通過(guò)添加環(huán)境變量可以打印出APP的啟動(dòng)時(shí)間分析(Edit scheme -> Run -> Arguments->Environment Varlables),DYLD_PRINT_STATISTICS設(shè)置為1,如果需要更詳細(xì)的信息,那就將DYLD_PRINT_STATISTICS_DETAILS設(shè)置為1。

可以看見(jiàn)控制臺(tái)打印啟動(dòng)過(guò)程各個(gè)階段所占時(shí)間長(zhǎng)及比例。

APP啟動(dòng)3大階段

APP的冷啟動(dòng)可以概括為3大階段:dyld、runtime、main。

dyld(dynamic link editor),Apple的動(dòng)態(tài)鏈接器,可以用來(lái)裝載Mach-O文件(可執(zhí)行文件、動(dòng)態(tài)庫(kù)等),啟動(dòng)APP時(shí),dyld所做的事情有:裝載APP的可執(zhí)行文件,同時(shí)會(huì)遞歸加載所有依賴(lài)的動(dòng)態(tài)庫(kù)

當(dāng)dyld把可執(zhí)行文件、動(dòng)態(tài)庫(kù)都裝載完畢后,會(huì)通知Runtime進(jìn)行下一步的處理。

啟動(dòng)APP時(shí),runtime所做的事情有:調(diào)用map_images進(jìn)行可執(zhí)行文件內(nèi)容的解析和處理

在load_images中調(diào)用call_load_methods,調(diào)用所有Class和Category的+load方法,進(jìn)行各種objc結(jié)構(gòu)的初始化(注冊(cè)O(shè)bjc類(lèi) 、初始化類(lèi)對(duì)象等等),調(diào)用C++靜態(tài)初始化器和attribute((constructor))修飾的函數(shù),

到此為止,可執(zhí)行文件和動(dòng)態(tài)庫(kù)中所有的符號(hào)(Class,Protocol,Selector,IMP,…)都已經(jīng)按格式成功加載到內(nèi)存中,被runtime 所管理。

APP的啟動(dòng) - main:調(diào)用UIApplicationMain函數(shù),加載完成后會(huì)調(diào)用AppDelegate的application:didFinishLaunchingWithOptions:方法。

總結(jié)一下
APP的啟動(dòng)由dyld主導(dǎo),將可執(zhí)行文件加載到內(nèi)存,順便加載所有依賴(lài)的動(dòng)態(tài)庫(kù);

并由runtime負(fù)責(zé)加載成objc定義的結(jié)構(gòu);

所有初始化工作結(jié)束后,dyld就會(huì)調(diào)用main函數(shù);

接下來(lái)就是UIApplicationMain函數(shù),AppDelegate的application:didFinishLaunchingWithOptions:方法。

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

按照不同的階段

dyld:減少動(dòng)態(tài)庫(kù)、合并一些動(dòng)態(tài)庫(kù)(定期清理不必要的動(dòng)態(tài)庫(kù));減少Objc類(lèi)、分類(lèi)的數(shù)量、減少Selector數(shù)量(定期清理不必要的類(lèi)、分類(lèi));減少C++虛函數(shù)數(shù)量;Swift盡量使用struct。

untime:用+initialize方法和dispatch_once取代所有的attribute((constructor))、C++靜態(tài)構(gòu)造器、ObjC的+load。

main:在不影響用戶(hù)體驗(yàn)的前提下,盡可能將一些操作延遲,不要全部都放在finishLaunching方法中

按需加載。

安裝包瘦身

安裝包(IPA)主要由可執(zhí)行文件、資源(圖片、音頻、視頻等)組成。

采取無(wú)損壓縮,去除沒(méi)有用到的資源: https://github.com/tinymind/LSUnusedResources;

可執(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(https://www.jetbrains.com/objc/ )檢測(cè)未使用的代碼:菜單欄 -> Code -> Inspect Code

編寫(xiě)LLVM插件檢測(cè)出重復(fù)代碼、未被調(diào)用的代碼;

生成LinkMap文件,可以查看可執(zhí)行文件的具體組成;

可借助第三方工具解析LinkMap文件: https://github.com/huanxsd/LinkMap

面試題
你在項(xiàng)目中是怎么優(yōu)化內(nèi)存的?
用ARC管理內(nèi)存、在正確的地方使用reuseIdentifier、當(dāng)View設(shè)置為透明的時(shí)候,一般把a(bǔ)paque設(shè)為NO,減小開(kāi)銷(xiāo),盡量不要離屏渲染,對(duì)內(nèi)存有好處、避免龐大的XIB、不要阻塞主線(xiàn)程、懶加載、Cache 緩存等等。

列表(UITableView)卡頓的原因可能有哪些?你平時(shí)是怎么優(yōu)化的?

最常用的就是cell的重用, 注冊(cè)重用標(biāo)識(shí)符;

如果不重用cell時(shí),每當(dāng)一個(gè)cell顯示到屏幕上時(shí),就會(huì)重新創(chuàng)建一個(gè)新的cell

如果有很多數(shù)據(jù)的時(shí)候,就會(huì)堆積很多cell。

如果重用cell,為cell創(chuàng)建一個(gè)ID,每當(dāng)需要顯示cell 的時(shí)候,都會(huì)先去緩沖池中尋找可循環(huán)利用的cell,如果沒(méi)有再重新創(chuàng)建cell

避免cell的重新布局

cell的布局填充等操作 比較耗時(shí),一般創(chuàng)建時(shí)就布局好

如可以將cell單獨(dú)放到一個(gè)自定義類(lèi),初始化時(shí)就布局好

提前計(jì)算并緩存cell的屬性及內(nèi)容

當(dāng)我們創(chuàng)建cell的數(shù)據(jù)源方法時(shí),編譯器并不是先創(chuàng)建cell 再定cell的高度,而是先根據(jù)內(nèi)容一次確定每一個(gè)cell的高度,高度確定后,再創(chuàng)建要顯示的cell,滾動(dòng)時(shí),每當(dāng)cell進(jìn)入憑虛都會(huì)計(jì)算高度,提前估算高度告訴編譯器,編譯器知道高度后,緊接著就會(huì)創(chuàng)建cell,這時(shí)再調(diào)用高度的具體計(jì)算方法,這樣可以方式浪費(fèi)時(shí)間去計(jì)算顯示以外的cell

減少cell中控件的數(shù)量

盡量使cell得布局大致相同,不同風(fēng)格的cell可以使用不用的重用標(biāo)識(shí)符,初始化時(shí)添加控件,不需要使用的可以先隱藏;

不要使用ClearColor,無(wú)背景色,透明度也不要設(shè)置為0

渲染耗時(shí)比較長(zhǎng)

使用局部更新

如果只是更新某組的話(huà),使用reloadSection進(jìn)行局部更

加載網(wǎng)絡(luò)數(shù)據(jù),下載圖片,使用異步加載,并緩存

少使用addView 給cell動(dòng)態(tài)添加view

按需加載cell,cell滾動(dòng)很快時(shí),只加載范圍內(nèi)的cell

不要實(shí)現(xiàn)無(wú)用的代理方法,tableView只遵守兩個(gè)協(xié)議

緩存行高:estimatedHeightForRow不能和HeightForRow里面的layoutIfNeed同時(shí)存在,這兩者同時(shí)存在才會(huì)出現(xiàn)“竄動(dòng)”的bug。所以我的建議是:只要是固定行高就寫(xiě)預(yù)估行高來(lái)減少行高調(diào)用次數(shù)提升性能。如果是動(dòng)態(tài)行高就不要寫(xiě)預(yù)估方法了,用一個(gè)行高的緩存字典來(lái)減少代碼的調(diào)用次數(shù)即可

不要做多余的繪制工作。在實(shí)現(xiàn)drawRect:的時(shí)候,它的rect參數(shù)就是需要繪制的區(qū)域,這個(gè)區(qū)域之外的不需要進(jìn)行繪制。例可以用CGRectIntersectsRect、CGRectIntersection或CGRectContainsRect判斷是否需要繪制image和text,然后再調(diào)用繪制方法。

預(yù)渲染圖像。當(dāng)新的圖像出現(xiàn)時(shí),仍然會(huì)有短暫的停頓現(xiàn)象。解決的辦法就是在bitmap context里先將其畫(huà)一遍,導(dǎo)出成UIImage對(duì)象,然后再繪制到屏幕;

使用正確的數(shù)據(jù)結(jié)構(gòu)來(lái)存儲(chǔ)數(shù)據(jù)。

————————————————
以下文章可以做一個(gè)學(xué)習(xí)參考:
GCD面試要點(diǎn)
block面試要點(diǎn)
Runtime面試要點(diǎn)
RunLoop面試要點(diǎn)
內(nèi)存管理面試要點(diǎn)
MVC、MVVM面試要點(diǎn)
網(wǎng)絡(luò)性能優(yōu)化面試要點(diǎn)
網(wǎng)絡(luò)編程面試要點(diǎn)
KVC&KVO面試要點(diǎn)
數(shù)據(jù)存儲(chǔ)面試要點(diǎn)
混編技術(shù)面試要點(diǎn)
設(shè)計(jì)模式面試要點(diǎn)
UI面試要點(diǎn)

?著作權(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)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

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