CALayer的shouldRasterize屬性可能很多人都沒有聽說過,即使是聽說過也不知道這個屬性開啟后有什么意義,有什么應用場景。今天我們就來好好來說道說道。
1、何謂光柵化
rasterize,中文是光柵化。這個翻譯反而讓人更加疑惑了,光柵化是個什么東西。
我們先來看看shouldRasterize在蘋果官方文檔中的解釋:
A Boolean that indicates whether the layer is rendered as a bitmap before compositing. Animatable
指示layer在合成之前是否被渲染為位圖的布爾值??蓜赢?/p>
也就是說所謂光柵化就是把layer轉(zhuǎn)化為位圖的過程。
至于何為位圖,可以看維基百科的解釋(可能要翻墻)。
合成,是一個將不同位圖放到一起來創(chuàng)建你最終在屏幕上看到圖像的過程。
實際上我們可以把位圖理解為圖層樹或者渲染樹某個層級計算后得到的最終結(jié)果。合成完成后,最終把多張位圖合成得到一張位圖。
2、渲染過程
要徹底理解光柵化,還要理解iOS中界面的渲染過程。渲染是一個復雜的過程,要想說明白需要大量篇幅,我們這里長話短說:
首先CPU計算要顯示的內(nèi)容(包括創(chuàng)建視圖,布局計算,圖片解碼等),得到圖層樹,然后將圖層樹通過一系列計算轉(zhuǎn)成渲染樹,渲染樹的信息會提交給GPU。GPU通過6個階段的工作后,將CPU和GPU計算后的數(shù)據(jù)顯示在屏幕的每個像素點上。這6個階段中就有光柵化。
理解渲染過程對于理解光柵化還是很重要的,這里有幾篇文章供你參考:繪制像素到屏幕上. objc.io中國.
、ibireme. iOS 保持界面流暢的技巧.、如何理解光柵化等概念.知乎
3、開啟shouldRasterize意味著什么。
3.1 減少視圖層級
設置一個CALayer的shouldRasterize=YES,相當于提前把一個CALayer光柵化。這個CALayer圖層結(jié)構(gòu)就被拍扁了,變成一張位圖。
3.2 緩存得到圖像,提高性能
啟用shouldRasterize屬性會將圖層繪制到一個屏幕之外的圖像,然后這個圖像將會被緩存起來。緩存起來是一個重要的特性,它幾乎決定了shouldRasterize的所有應用場景。
如果有很多的子圖層或者有復雜的效果應用,開啟shouldRasterize就會比重繪所有事務的所有幀劃得來得多。但是光柵化原始圖像需要時間,而且還會消耗額外的內(nèi)存。所以需要根據(jù)實際情況取舍。
4、shouldRasterize的應用場景
通過上面shouldRasterize的理解,就不難得出shouldRasterize應該如何應用。
4.1 啟用shouldRasterize提升復雜層級視圖的性能
由于啟用shouldRasterize得到的圖像會被緩存起來。這大大減少了GPU的負擔。試想在一個table view的cell中,有非常復雜的層級結(jié)構(gòu)?;瑒觮ableview的時候,GPU需要進行大量的合成,這有可能會導致性能問題。我們可以嘗試將cell的layer的shouldRasterize打開提升性能。
4.2 啟用shouldRasterize提升動畫性能
對一個層級復雜的視圖做動畫時,也可以啟用shouldRasterize避免GPU每幀都重新合成。
4.3 啟用shouldRasterize改善離屏渲染的性能。
仍然是在一個table view的cell中,如果使用了陰影效果,那么會觸發(fā)離屏渲染:
//dequeue cell
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];
...
//set text shadow
cell.textLabel.backgroundColor = [UIColor clearColor];
cell.textLabel.layer.shadowOffset = CGSizeMake(0, 2);
cell.textLabel.layer.shadowOpacity = 0.5;”
每一行的字符和頭像在每一幀刷新的時候并不需要變,所以看起來UITableViewCell的圖層非常適合做緩存。我們可以使用shouldRasterize來緩存圖層內(nèi)容。這將會讓圖層離屏之后渲染一次然后把結(jié)果保存起來,直到下次利用的時候去更新:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//dequeue cell
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];
...
//set text shadow
cell.textLabel.backgroundColor = [UIColor clearColor];
cell.textLabel.layer.shadowOffset = CGSizeMake(0, 2);
cell.textLabel.layer.shadowOpacity = 0.5;
//rasterize
cell.layer.shouldRasterize = YES;
cell.layer.rasterizationScale = [UIScreen mainScreen].scale;
return cell;
}
4.4 實際測量而不是猜測
在使用上述手段優(yōu)化之前,首先要通過實際真機測試,確定出現(xiàn)了性能問題。優(yōu)化之后,還要真機測試,確認解決了性能問題。
在一個經(jīng)常會變化的layer中開啟shouldRasterize是沒有多大意義的,因為緩存的總是會被重新創(chuàng)建,而光柵化還要消耗性能,沒準適得其反。
下面是開發(fā)工具提供的幾個常用的測量方式:
Instrument的Core Animation可以監(jiān)控Core Animation的性能,我們可以用它監(jiān)控FPS等指標。
Xcode->Debug->View Debuging->Rendering->Color Offscrenn-rended Yellow可以為觸發(fā)了離屏渲染的layer著黃色。Xcode->Debug->View Debuging->Rendering->Hits Green and Misses Red
這個指標可以反映我們通過嘗試開啟shouldRasterize提升性能的時候是不是達到了預期效果。
當使用shouldRasterize屬性的時候,耗時的圖層繪制會被緩存,然后當做一個簡單的扁平圖片呈現(xiàn)。當緩存再生的時候這個選項就用紅色對柵格化圖層進行了高亮,緩存被重復使用的話就會以綠色進行高亮。如果緩存頻繁再生的話(紅色太多),就意味著柵格化可能會有負面的性能影響了。
參考:
[4] 戴銘.iOS開發(fā)高手課第42講《iOS原生、大前端和Flutter分別是怎么渲染的?.極客時間
[6] Nick Lockwood. iOS Core Animation: Advanced Techniques 中文譯本