iOS APP渲染性能優(yōu)化

本文講述在APP的開(kāi)發(fā)過(guò)程中,需要從哪些方面對(duì)渲染性能進(jìn)行優(yōu)化。

1.盡量避免使用半透明

1.1原因

在使用半透明時(shí),會(huì)發(fā)生Color blending現(xiàn)象。在渲染的時(shí)候,為了得到某個(gè)像素的最終的顏色值,系統(tǒng)將不得不將當(dāng)前Layer和它下方的Layer進(jìn)行顏色的混合,這樣就增加了計(jì)算量。如果非透明,那么系統(tǒng)只需要根據(jù)當(dāng)前Layer,就能得到最終的顏色值。

1.2檢測(cè)方法

在模擬器運(yùn)行時(shí),可以通過(guò)Debug菜單項(xiàng),勾選'Color Blended Layers'。


menu_color_blending.png

在設(shè)備上運(yùn)行時(shí),可以選擇Instruments中的Core Animation模板,然后在Debug Options中開(kāi)啟'Color Blended Layers',如圖:


WX20171101-152631@2x.png

檢測(cè)結(jié)果如下圖所示,中間的視圖處于'blending enabled'狀態(tài),被高亮為紅色。


color_blend_result.png

1.3解決辦法

Color blending可由下面的方式觸發(fā):

  • UIView的alpha被設(shè)置為小于1大于0的數(shù)
  • UIView的backgroundColor的alpha通道的值為小于1大于0的數(shù)
  • CALayer的opacity被設(shè)置為小于1大于0的數(shù)
  • UIImageView使用的圖片資源中有透明像素

實(shí)際開(kāi)發(fā)中,不太可能完全避免上面列出的觸發(fā)原因,這種情況下,我們要注意避免對(duì)需要頻繁更新的視圖使用半透明。

2.UIImageView的大小和資源大小一致;UILabel的高度要為整數(shù)

2.1原因

  • 當(dāng)UIImageView的大小和圖片資源的大小不一致時(shí),會(huì)發(fā)生'Misaligned Images'問(wèn)題,導(dǎo)致源像素和目的像素不能對(duì)齊。
  • 當(dāng)UILabel的高度為非整數(shù)的像素值時(shí),同樣會(huì)發(fā)生'Misaligned Images'問(wèn)題。

Misaligned Image表示要繪制的點(diǎn)無(wú)法直接映射到屏幕上的像素點(diǎn),此時(shí)系統(tǒng)需要對(duì)相鄰的像素點(diǎn)做anti-aliasing反鋸齒計(jì)算,增加了圖形負(fù)擔(dān),通常這種問(wèn)題出在對(duì)某些View的Frame重新計(jì)算和設(shè)置時(shí)產(chǎn)生的。

2.2檢測(cè)方法

在模擬器運(yùn)行時(shí),可以通過(guò)Debug菜單項(xiàng),勾選'Color Misaligned Images'選項(xiàng)。
在設(shè)備上運(yùn)行時(shí),可以選擇Instruments中的CoreAnimation模板,然后在Debug Options中開(kāi)啟'Color Misaligned Images。
檢測(cè)結(jié)果如下,對(duì)于UILabel,高亮為洋紅色;對(duì)于UIImageView,高亮為黃色。


misaligned_images.png

2.3解決辦法

Misaligned Images可由下面的原因?qū)е拢?/p>

  • 硬編碼的UILabel的高度值,轉(zhuǎn)換為像素值后,不為整數(shù)。例如10.3在@2x, @3x的設(shè)備上均不為整數(shù)。10.5在@2x上為整數(shù),在@3x上不為整數(shù)。
  • 使用NSString或NSAttributedString的boundingRectWithSize函數(shù)計(jì)算出來(lái)的高度值,沒(méi)有進(jìn)行向上取整,導(dǎo)致出現(xiàn)非整數(shù)值。
  • 圖片資源的大小,除以相應(yīng)的設(shè)備屏幕的scale,出現(xiàn)了非整數(shù)。例如在@2x的設(shè)備上,圖片的寬為101。
  • 圖片資源的大小,沒(méi)有保持嚴(yán)格的scale比例。例如@2x的大小為40x40,然而@3x的大小卻不是60x60。
  • 圖片資源沒(méi)有提供全部scale(@2x, @3x)版本
  • 圖片是網(wǎng)絡(luò)下載的,例如在使用SDWebImage時(shí)。

除了最后一個(gè)原因,都很好解決。

3.不要使用非32bit顏色格式的圖片資源

3.1原因

蘋果的GPU只解析32bit的顏色格式。如果一張圖片的顏色格式不是32bit,CPU會(huì)先進(jìn)行顏色格式轉(zhuǎn)換,再讓GPU渲染,這樣就加重了CPU的負(fù)擔(dān)。
蘋果官方稱這種現(xiàn)象為'Copied Images'。

3.2檢測(cè)方法

在模擬器和真機(jī)上都可以檢測(cè)出來(lái),勾選'Color Copied Images'選項(xiàng)即可。
示例如下,圖中的Mario圖片是8bit的png圖片,被檢測(cè)出并高亮顯示。


copied_images.png

3.3解決辦法

此問(wèn)題比較容易解決,對(duì)于設(shè)計(jì)師導(dǎo)出的圖片資源,應(yīng)該是32bit的顏色。
另外發(fā)現(xiàn)即使是一個(gè)8 bit的png圖,如果通過(guò)Assets進(jìn)行管理,則也不會(huì)有問(wèn)題,只有當(dāng)圖片被以傳統(tǒng)的方式加入到項(xiàng)目里時(shí),才會(huì)出現(xiàn)此問(wèn)題。
這也再次說(shuō)明了,圖片資源應(yīng)該盡可能的都通過(guò)Assets進(jìn)行管理。

4.不要直接使用layer.cornerRadius和layer.masksToBounds設(shè)置圖片的圓角

注意:此條僅對(duì)iOS9以下系統(tǒng)有效。從iOS9開(kāi)始,直接使用上面的兩個(gè)屬性設(shè)置圓角,不會(huì)有任何性能問(wèn)題。

4.1原因

使用這兩個(gè)屬性設(shè)置圖片的圓角時(shí),會(huì)觸發(fā)離屏渲染: Offscreen rendering。所謂離屏渲染,就是不能由GPU執(zhí)行,必須由CPU執(zhí)行的渲染。在渲染的過(guò)程中,一旦檢測(cè)到需要離屏渲染,那么需要從GPU切換到CPU,在執(zhí)行完后還要再切換回GPU。而CPU在執(zhí)行離屏渲染的時(shí)候,還需要分配額外的內(nèi)存。

4.2檢測(cè)方法

在模擬器和Instruments中都有'Color Offscreen-Rendered Yellow'選項(xiàng),勾選即可進(jìn)行檢測(cè)。
檢測(cè)結(jié)果如下,被離屏渲染的UIImageView被黃色高亮顯示。


IMG_5950.PNG

4.3解決辦法

要實(shí)現(xiàn)圓角效果,但又不想觸發(fā)離屏渲染,可以基于原始圖片,得到一個(gè)圓角的圖片,然后使用。得到圓角圖片的過(guò)程雖然有一定的開(kāi)銷,但是只需要付出一次代價(jià),而離屏渲染卻是每幀都要付出代價(jià)。
使用下面的UIImage的擴(kuò)展方法,可以方便的得到圓角圖片:

@implementation UIImage (UIImageCornerRadius)

- (UIImage *)imageWithRoundedCornersAndSize:(CGSize)sizeToFit  {
    CGRect rect = (CGRect){0.f, 0.f, sizeToFit};
    
    UIGraphicsBeginImageContextWithOptions(sizeToFit, NO, UIScreen.mainScreen.scale);
    CGContextAddPath(UIGraphicsGetCurrentContext(),
                     [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:sizeToFit.width].CGPath);
    CGContextClip(UIGraphicsGetCurrentContext());
    
    [self drawInRect:rect];
    UIImage *output = UIGraphicsGetImageFromCurrentImageContext();
    
    UIGraphicsEndImageContext();
    
    return output;
}

@end

5.設(shè)置layer的陰影時(shí),顯式的指定shadowPath

5.1原因

如果我們用如下代碼去設(shè)置陰影效果,那么就會(huì)觸發(fā)離屏渲染。

UIView *view = [[UIView alloc] initWithFrame: CGRectMake(100, 100, 100, 100)];
view.layer.shadowRadius = 30;
view.layer.shadowOpacity = 0.5f;
view.layer.shadowColor = [UIColor blackColor].CGColor;
view.layer.shadowOffset = CGSizeMake(0, 6);

蘋果是這么講的:

“Letting Core Animation determine the shape of a shadow can be expensive and impact your app’s performance. Rather than letting Core Animation determine the shape of the shadow, specify the shadow shape explicitly using the shadowPath property of CALayer. When you specify a path object for this property, Core Animation uses that shape to draw and cache the shadow effect. For layers, whose shape never changes or rarely changes, this greatly improves the performance by reducing the amount of rendering done by Core Animation.”

翻譯過(guò)來(lái)就是說(shuō),讓Core Animation去決定陰影的形狀,是很昂貴的并且會(huì)影響應(yīng)用的性能。開(kāi)發(fā)者應(yīng)該顯式的去指定陰影的形狀,而這可以通過(guò)shadowPath這個(gè)屬性來(lái)輕易的實(shí)現(xiàn)。

5.2解決辦法

不要使用shadowRadius這個(gè)屬性,改為使用shadowPath。將上面的代碼改為:

UIView *view = [[UIView alloc] initWithFrame: CGRectMake(100, 100, 100, 100)];
view.layer.shadowOpacity = 0.5f;
view.layer.shadowColor = [UIColor blackColor].CGColor;
view.layer.shadowPath = [UIBezierPath bezierPathWithRect: CGRectOffset(view.bounds, 0, 6)].CGPath;

下圖顯示了在開(kāi)啟'Color Offscreen-Rendered Yellow'選項(xiàng)的情況下,兩種寫法的對(duì)比結(jié)果。


shadowPath.png

上方的使用了shadowRadius,被標(biāo)注為離屏渲染。下方的使用了shadowPath,不會(huì)發(fā)生離屏渲染。

6.其它注意事項(xiàng)

  • 一般不要去修改UIView.opaque屬性的默認(rèn)值(YES)。
  • 視圖要盡可能的少,不要?jiǎng)?chuàng)建不必要的視圖。
  • 不到迫不得已,不要通過(guò)override drawRect:來(lái)實(shí)現(xiàn)自定義的視圖。

7.參考

Mastering UIKit Performance
WWDC 2014 Video: Advanced Graphics and Animations for iOS Apps

最后編輯于
?著作權(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)容