優(yōu)化方案
官方對離屏渲染產(chǎn)生性能問題也進行了優(yōu)化:
iOS 9.0 之前UIimageView跟UIButton設置圓角都會觸發(fā)離屏渲染。
iOS 9.0 之后UIButton設置圓角會觸發(fā)離屏渲染,而UIImageView里png圖片設置圓角不會觸發(fā)離屏渲染了,如果設置其他陰影效果之類的還是會觸發(fā)離屏渲染的。
1、圓角優(yōu)化
在APP開發(fā)中,圓角圖片還是經(jīng)常出現(xiàn)的。如果一個界面中只有少量圓角圖片或許對性能沒有非常大的影響,但是當圓角圖片比較多的時候就會APP性能產(chǎn)生明顯的影響。
我們設置圓角一般通過如下方式:
imageView.layer.cornerRadius = CGFloat(10);
imageView.layer.masksToBounds = YES;
這樣處理的渲染機制是GPU在當前屏幕緩沖區(qū)外新開辟一個渲染緩沖區(qū)進行工作,也就是離屏渲染,這會給我們帶來額外的性能損耗,如果這樣的圓角操作達到一定數(shù)量,會觸發(fā)緩沖區(qū)的頻繁合并和上下文的的頻繁切換,性能的代價會宏觀地表現(xiàn)在用戶體驗上——掉幀。
優(yōu)化方案1:使用貝塞爾曲線UIBezierPath和Core Graphics框架畫出一個圓角
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(100,100,100,100)];
imageView.image = [UIImage imageNamed:@"myImg"];
//開始對imageView進行畫圖
UIGraphicsBeginImageContextWithOptions(imageView.bounds.size,NO,1.0);
//使用貝塞爾曲線畫出一個圓形圖
[[UIBezierPath bezierPathWithRoundedRect:imageView.boundscornerRadius:imageView.frame.size.width]addClip];
[imageView drawRect:imageView.bounds];
imageView.image=UIGraphicsGetImageFromCurrentImageContext();
//結束畫圖
UIGraphicsEndImageContext();
[self.view addSubview:imageView];
優(yōu)化方案2:使用CAShapeLayer和UIBezierPath設置圓角
UIImageView *imageView = [[UIImageView alloc]initWithFrame:CGRectMake(100, 100, 100, 100)];
imageView.image = [UIImage imageNamed:@"myImg"];
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:imageView.bounds byRoundingCorners:UIRectCornerAllCorners cornerRadii:imageView.bounds.size];
CAShapeLayer *maskLayer = [[CAShapeLayer alloc]init];
//設置大小
maskLayer.frame = imageView.bounds;
//設置圖形樣子
maskLayer.path = maskPath.CGPath;
imageView.layer.mask = maskLayer;
[self.view addSubview:imageView];
對于方案2需要解釋的是:
CAShapeLayer繼承于CALayer,可以使用CALayer的所有屬性值;
CAShapeLayer需要貝塞爾曲線配合使用才有意義(也就是說才有效果)
使用CAShapeLayer(屬于CoreAnimation)與貝塞爾曲線可以實現(xiàn)不在view的drawRect(繼承于CoreGraphics走的是CPU,消耗的性能較大)方法中畫出一些想要的圖形
CAShapeLayer動畫渲染直接提交到手機的GPU當中,相較于view的drawRect方法使用CPU渲染而言,其效率極高,能大大優(yōu)化內存使用情況。
總的來說就是用CAShapeLayer的內存消耗少,渲染速度快,建議使用優(yōu)化方案2。
2、shadow優(yōu)化
對于shadow,如果圖層是個簡單的幾何圖形或者圓角圖形,我們可以通過設置shadowPath來優(yōu)化性能,能大幅提高性能。示例如下:
imageView.layer.shadowColor=[UIColorgrayColor].CGColor;
imageView.layer.shadowOpacity=1.0;
imageView.layer.shadowRadius=2.0;
UIBezierPath *path=[UIBezierPathbezierPathWithRect:imageView.frame];
imageView.layer.shadowPath=path.CGPath;
我們還可以通過設置shouldRasterize屬性值為YES來強制開啟離屏渲染。其實就是光柵化(Rasterization)。既然離屏渲染這么不好,為什么我們還要強制開啟呢?當一個圖像混合了多個圖層,每次移動時,每一幀都要重新合成這些圖層,十分消耗性能。當我們開啟光柵化后,會在首次產(chǎn)生一個位圖緩存,當再次使用時候就會復用這個緩存。但是如果圖層發(fā)生改變的時候就會重新產(chǎn)生位圖緩存。所以這個功能一般不能用于UITableViewCell中,cell的復用反而降低了性能。最好用于圖層較多的靜態(tài)內容的圖形。而且產(chǎn)生的位圖緩存的大小是有限制的,一般是2.5個屏幕尺寸。在100ms之內不使用這個緩存,緩存也會被刪除。所以我們要根據(jù)使用場景而定。
3、其他的一些優(yōu)化建議
當我們需要圓角效果時,可以使用一張中間透明圖片蒙上去
使用ShadowPath指定layer陰影效果路徑
使用異步進行l(wèi)ayer渲染(Facebook開源的異步繪制框架AsyncDisplayKit)
設置layer的opaque值為YES,減少復雜圖層合成
盡量使用不包含透明(alpha)通道的圖片資源
盡量設置layer的大小值為整形值
直接讓美工把圖片切成圓角進行顯示,這是效率最高的一種方案
很多情況下用戶上傳圖片進行顯示,可以讓服務端處理圓角
使用代碼手動生成圓角Image設置到要顯示的View上,利用UIBezierPath(CoreGraphics框架)畫出來圓角圖片
Core Animation工具檢測離屏渲染
對于離屏渲染的檢測,蘋果為我們提供了一個測試工具Core Animation??梢栽赬code->Open Develeper Tools->Instruments中找到

Core Animation工具用來監(jiān)測Core Animation性能,提供可見的FPS值,并且提供幾個選項來測量渲染性能。如下圖:

下面我們來說明每個選項的功能:
Color Blended Layers:這個選項如果勾選,你能看到哪個layer是透明的,GPU正在做混合計算。顯示紅色的就是透明的,綠色就是不透明的。
Color Hits Green and Misses Red:如果勾選這個選項,且當我們代碼中有設置shouldRasterize為YES,那么紅色代表沒有復用離屏渲染的緩存,綠色則表示復用了緩存。我們當然希望能夠復用。
Color Copied Images:按照官方的說法,當圖片的顏色格式GPU不支持的時候,Core Animation會
拷貝一份數(shù)據(jù)讓CPU進行轉化。例如從網(wǎng)絡上下載了TIFF格式的圖片,則需要CPU進行轉化,這個區(qū)域會顯示成藍色。還有一種情況會觸發(fā)Core Animation的copy方法,就是字節(jié)不對齊的時候。如下圖:

Color Immediately:默認情況下Core Animation工具以每毫秒10次的頻率更新圖層調試顏色,如果勾選這個選項則移除10ms的延遲。對某些情況需要這樣,但是有可能影響正常幀數(shù)的測試。
Color Misaligned Images:勾選此項,如果圖片需要縮放則標記為黃色,如果沒有像素對齊則標記為紫色。像素對齊我們已經(jīng)在上面有所介紹。
Color Offscreen-Rendered Yellow:用來檢測離屏渲染的,如果顯示黃色,表示有離屏渲染。當然還要結合Color Hits Green and Misses Red來看,是否復用了緩存。
Color OpenGL Fast Path Blue:這個選項對那些使用OpenGL的圖層才有用,像是GLKView或者 CAEAGLLayer,如果不顯示藍色則表示使用了CPU渲染,繪制在了屏幕外,顯示藍色表示正常。
Flash Updated Regions:當對圖層重繪的時候回顯示黃色,如果頻繁發(fā)生則會影響性能??梢杂迷黾泳彺鎭碓鰪娦阅?。