歡迎加入QQ討論群258996829
麥子學(xué)院 頭像
蘋果6袋
6
麥子學(xué)院

UIKit如何進(jìn)行性能調(diào)優(yōu)(續(xù))

發(fā)布時(shí)間:2016-06-16 23:27  回復(fù):0  查看:2934   最后回復(fù):2016-06-16 23:27  

上一篇我們說過,在使用 UIKit框架 的過程中,性能優(yōu)化是永恒的話題 。 也為大家講了部分 UIKit性能調(diào)優(yōu) 的知識(shí),這篇文章將繼續(xù)為大家用實(shí)戰(zhàn)詳細(xì)講解。
顏色格式
像素在內(nèi)存中的布局和它在磁盤中的存儲(chǔ)方式并不相同??紤]一種簡單的情況:每個(gè)像素有 R 、 G 、 B alpha 四個(gè)值,每個(gè)值占用 1 字節(jié),因此每個(gè)像素占用 4 字節(jié)的內(nèi)存空間。一張 1920*1080 的照片 (iPhone6 Plus 的分辨率 ) 一共有 2,073,600 個(gè)像素,因此占用了超過 8Mb 的內(nèi)存。但是一張同樣分辨率的 PNG 格式或 JPEG 格式的圖片一般情況下不會(huì)有這么大。這是因?yàn)?/span> JPEG 將像素?cái)?shù)據(jù)進(jìn)行了一種非常復(fù)雜且可逆的轉(zhuǎn)化。
當(dāng)我們打開 JPEG 格式的圖片時(shí), CPU 會(huì)進(jìn)行一系列運(yùn)算,將 JPEG 圖片解壓成像素?cái)?shù)據(jù)。顯然這個(gè)工作會(huì)消耗不少時(shí)間,所以不應(yīng)該在滑動(dòng)時(shí)進(jìn)行,我們應(yīng)該預(yù)先處理好圖片。借用 WWDC 上的一頁 PPT 來說明:
UIKit如何進(jìn)行性能調(diào)優(yōu)(續(xù))
顯示流程
Commit Transaction Decode 在同一幀內(nèi)進(jìn)行,如果這兩個(gè)操作的耗時(shí)超過 16.67s , Draw Calls 就會(huì)延遲到下一幀,從而導(dǎo)致 fps 值的降低。下面是 Commit Transaction 的詳細(xì)流程:
UIKit如何進(jìn)行性能調(diào)優(yōu)(續(xù))
解碼與轉(zhuǎn)換
在第三步的 Prepare 中, CPU 主要處理兩件事:
把圖片從 PNG JPEG 等格式中解壓出來,得到像素?cái)?shù)據(jù)。
如果 GPU 不支持這種顏色各式, CPU 需要進(jìn)行格式轉(zhuǎn)換。
比如應(yīng)用中有一些從網(wǎng)絡(luò)下載的圖片,而 GPU 恰好不支持這個(gè)格式,這就需要 CPU 預(yù)先進(jìn)行格式轉(zhuǎn)化。第三個(gè)選項(xiàng) “Color Copied Images” 就用來檢測這種實(shí)時(shí)的格式轉(zhuǎn)化,如果有則會(huì)將圖片標(biāo)記為藍(lán)色。
遺憾的是由于我對圖片格式不太了解,也不會(huì)使用相關(guān)工具,并沒有能模擬出觸發(fā)這個(gè)選項(xiàng)的場景。我們要記住的是,如果調(diào)試時(shí)發(fā)現(xiàn)有圖片被標(biāo)記為藍(lán)色,說明圖片格式出現(xiàn)了一些問題。
圖片大小
第四個(gè)選項(xiàng)的使用場景不多,我們直接看一下第五個(gè)選項(xiàng) “Color Misaligned Images” 。它表示如果圖片需要縮放則標(biāo)記為**,如果沒有像素對齊則標(biāo)記為紫色。勾選上這個(gè)選項(xiàng)并進(jìn)行調(diào)試,可以看到如下場景:
UIKit如何進(jìn)行性能調(diào)優(yōu)(續(xù)) 
圖片縮放
demo 中,每個(gè) UIImageView 的大小都是 180x180 ,而只有第二張圖片的像素大小是 360x360 。因此除了第二張圖片,其他的圖片都需要被縮放。圖片的縮放需要占用時(shí)間,因此我們要盡可能保證無論是本地圖片還是從網(wǎng)絡(luò)或取得圖片的大小,都與其 frame 保持一致。
第三個(gè)優(yōu)化是調(diào)整所有圖片的像素大小以避免不必要的縮放。
離屏渲染
離屏渲染表示渲染發(fā)生在屏幕之外,你可能認(rèn)為這是一句廢話。為了真正解釋清楚什么是離屏渲染,我們先來看一下正常的渲染通道 (Render-Pass)
UIKit如何進(jìn)行性能調(diào)優(yōu)(續(xù))
正常渲染通道
首先, OpenGL 提交一個(gè)命令到 Command Buffer ,隨后 GPU 開始渲染,渲染結(jié)果放到 Render Buffer 中,這是正常的渲染流程。但是有一些復(fù)雜的效果無法直接渲染出結(jié)果,它需要分步渲染最后再組合起來,比如添加一個(gè)蒙版 (mask)
UIKit如何進(jìn)行性能調(diào)優(yōu)(續(xù))
離屏渲染
在前兩個(gè)渲染通道中, GPU 分別得到了紋理 (texture ,也就是那個(gè)相機(jī)圖標(biāo) ) layer( 藍(lán)色的蒙版 ) 的渲染結(jié)果。但這兩個(gè)渲染結(jié)果沒有直接放入 Render Buffer 中,也就表示這是離屏渲染。直到第三個(gè)渲染通道,才把兩者組合起來放入 Render Buffer 中。離屏渲染意味著把渲染結(jié)果臨時(shí)保存,等用到時(shí)再取出,因此相對于普通渲染更占用資源。
第六個(gè)選項(xiàng) “Color Offscreen-Rendered Yellow” 會(huì)把需要離屏渲染的地方標(biāo)記為**,大部分情況下我們需要盡可能避免**的出現(xiàn)。離屏渲染可能會(huì)自動(dòng)觸發(fā),也可以手動(dòng)觸發(fā)。以下情況可能會(huì)導(dǎo)致觸發(fā)離屏渲染:
重寫 drawRect 方法
mask 或者是陰影 (layer.masksToBounds, layer.shadow*) ,模糊效果也是一種 mask
layer.shouldRasterize = true
前兩者會(huì)自動(dòng)觸發(fā)離屏渲染,第三種方法是手動(dòng)開啟離屏渲染。
開始調(diào)試并勾選 “Color Offscreen-Rendered Yellow” ,會(huì)看到這樣的場景:
UIKit如何進(jìn)行性能調(diào)優(yōu)(續(xù)) 
離屏渲染
如果沒有進(jìn)行第二步優(yōu)化,你會(huì)發(fā)現(xiàn) label 也是**??梢钥吹?/span> tabbar statusBar 也是**,這是因?yàn)樗鼈兪褂昧四:Ч?。圖片也是**,這說明它也進(jìn)行了離屏渲染,觀察源碼后發(fā)現(xiàn)主要原因是它使用了陰影,接下來我們進(jìn)行第四個(gè)優(yōu)化,在設(shè)置陰影效果的四行代碼下面添加一行:
1
imgView.layer.shadowPath = UIBezierPath(rect: imgView.bounds).CGPath
這行代碼制定了陰影路徑,如果沒有手動(dòng)指定, Core Animation 會(huì)去自動(dòng)計(jì)算,這就會(huì)觸發(fā)離屏渲染。如果人為指定了陰影路徑,就可以免去計(jì)算,從而避免產(chǎn)生離屏渲染。
設(shè)置 cornerRadius 本身并不會(huì)導(dǎo)致離屏渲染,但很多時(shí)候它還需要配合 layer.masksToBounds = true 使用。根據(jù)之前的總結(jié),設(shè)置 masksToBounds 會(huì)導(dǎo)致離屏渲染。解決方案是盡可能在滑動(dòng)時(shí)避免設(shè)置圓角,如果必須設(shè)置圓角,可以使用光柵化技術(shù)將圓角緩存起來:
1
2
3
4
5
//  設(shè)置圓角
label.layer.masksToBounds = true
label.layer.cornerRadius = 8
label.layer.shouldRasterize = true
label.layer.rasterizationScale = layer.contentsScale
快速路徑
還記得之前將離屏渲染和渲染路徑時(shí)的示意圖么,離屏渲染的最后一步是把此前的多個(gè)路徑組合起來。如果這個(gè)組合過程能由 CPU 完成,就會(huì)大量減少 GPU 的工作。這種技術(shù)在繪制地圖中可能用到。
第七個(gè)選項(xiàng) “Color Compositing Fast-Path Blue” 用于標(biāo)記由硬件繪制的路徑,藍(lán)色越多越好。
變化區(qū)域
刷新視圖時(shí),我們應(yīng)該把需要重繪的區(qū)域盡可能縮小。對于未發(fā)生變化的內(nèi)容則不應(yīng)該重繪,第八個(gè)選項(xiàng) “Flash updated Regions” 用于標(biāo)記發(fā)生重繪的區(qū)域。一個(gè)典型的例子是系統(tǒng)的時(shí)鐘應(yīng)用,絕大多數(shù)時(shí)候只有顯示秒針的區(qū)域需要重繪:
UIKit如何進(jìn)行性能調(diào)優(yōu)(續(xù)) 
重繪區(qū)域
原文來自: bestswifter

您還未登錄,請先登錄

熱門帖子

最新帖子

?