亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

iOS如何實現圖形性能優化

發布時間:2021-12-24 15:33:26 來源:億速云 閱讀:233 作者:小新 欄目:移動開發

這篇文章將為大家詳細講解有關iOS如何實現圖形性能優化,小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。

引言

當一個產品漸漸成熟,我們便開始重視產品性能的優化。而這其中圖形性能的優化在iOS客戶端占比較重要的部分。

這里我們將介紹Core Animation的運行機制,首先我們不要被它的名字誤導了,Core Animation不是只用來做動畫的,iOS視圖的顯示都是通過它來完成的,所以我們想要優化圖形性能必須了解Core Animation。

下面我們根據蘋果WWDC視頻講解來認識Core Animation工作機制,據此分析具體卡頓的原因,如何避免這些問題造成的卡頓,并且結合實際情況說明從哪些方面優化可以事半功倍。

Core Animation 工作機制

iOS如何實現圖形性能優化

如上圖所示,Core Animation在App將圖層數據提交到應用外進程Render Server,這是Core Animation的服務端,把數據解碼成GPU可執行的指令交給GPU執行。

可以看出一個問題渲染服務并不是在App進程內進行的,也就是說渲染部分我們無法進行優化,我們可以優化的點只能在第一個提交事務的階段。那么這個階段Core Animation到底做了什么呢?下面我們一起來看看!

Commit Transaction

提交事務分為四個階段:布局、顯示、準備、提交。

iOS如何實現圖形性能優化

  • 布局階段

    當調用addSubview時layer被加入到layer tree中,layoutSubviews被調用,創建view。同時還會進行數據查找,例如app做了本地化,label要顯示這些本地化字符串必須從本地化文件中查找到對應語言的布局,這就涉及了I/O操作。所以這里主要是CPU工作,而瓶頸也會是CPU。

  • 顯示階段

    在這個階段如果你重寫了drawRect方法,Core Graphics會進行繪制渲染工作。為視圖繪制寄宿圖即contents。但是drawRect里繪制的內容不會立即顯示出來,而是先備換竄起來,等需要的時候被更新到屏幕上。如手動調用setNeedsDisplay或sizeThatFits被調用,也可以設置cententMode屬性值為UIViewContentModeRedraw當每次bounds改變會自動調用setNeedsDisplay方法。這個階段主要是CPU和內存的消耗,很多人喜歡用Core Graphics的方法來繪制圖形,認為可以提高性能,后面我們會說明這個方法的弊端。

  • 準備階段

    這里的工作主要是圖片的解碼,因為大部分都是編碼后的圖片,要讀取原始數據必須經過編碼過程。并且當我們使用了iOS不支持的圖片格式,即不支持硬編碼,就需要進行轉化工作,也是比較耗時的。所以這里就是GPU消耗,如果進行軟解碼也要消耗CPU。

  • 提交階段

    最后一個階段負責打包圖層數據并發送到我們上面說的渲染服務中。這個過程是一個遞歸操作,圖層樹越復雜越是需要消耗更多資源。像CALaler有很多隱式動畫屬性也會在這里提交,省去了多次動畫屬性進程間的交互,提高了性能。

優化

根據上面我們所提到4個階段,我們看看哪些因素會影響到App的性能,并且如何優化可以提高我們App的性能。

混合

平時我們寫代碼的時候,往往會給不同的CALayer添加不同的顏色,不同的透明度,我們最后看到是所有這些層CALayer混合出的結果。

那么在iOS中是如何進行混合的?前面我們說明了每個像素都包含了R(紅)、G(綠)、B(藍)和R(透明度),GPU要計算每個像素混合來的RGB值。那么如何計算這些顏色的混合值呢?假設在正常混合模式下,并且是像素對齊的兩個CALayer,混合計算公式如下:

R = S + D * ( 1 – Sa )

蘋果的文檔中有對每個參數的解釋:

The blend mode constants introduced in OS X v10.5   represent the Porter-Duff blend modes. The symbols in the   equations for these blend modes are:

    * R is the premultiplied result

    * S is the source color, and includes alpha

    * D is the destination color, and includes alpha

    * Ra, Sa, and Da are the alpha components of R, S, and D

R就是得到的結果色,S和D是包含透明度的源色和目標色,其實就是預先乘以透明度后的值。Sa就是源色的透明度。iOS為我們提供了多種的Blend mode:

 /* Available in Mac OS X 10.5 & later. R, S, and D are, respectively,
   premultiplied result, source, and destination colors with alpha; Ra,
   Sa, and Da are the alpha components of these colors.

   The Porter-Duff "source over" mode is called `kCGBlendModeNormal':
     R = S + D*(1 - Sa)

   Note that the Porter-Duff "XOR" mode is only titularly related to the
   classical bitmap XOR operation (which is unsupported by
   CoreGraphics). */

kCGBlendModeClear,                  /* R = 0 */
kCGBlendModeCopy,                   /* R = S */
kCGBlendModeSourceIn,               /* R = S*Da */
kCGBlendModeSourceOut,              /* R = S*(1 - Da) */
kCGBlendModeSourceAtop,             /* R = S*Da + D*(1 - Sa) */
kCGBlendModeDestinationOver,        /* R = S*(1 - Da) + D */
kCGBlendModeDestinationIn,          /* R = D*Sa */
kCGBlendModeDestinationOut,         /* R = D*(1 - Sa) */
kCGBlendModeDestinationAtop,        /* R = S*(1 - Da) + D*Sa */
kCGBlendModeXOR,                    /* R = S*(1 - Da) + D*(1 - Sa) */
kCGBlendModePlusDarker,             /* R = MAX(0, (1 - D) + (1 - S)) */
kCGBlendModePlusLighter             /* R = MIN(1, S + D) */

似乎計算也不是很復雜,但是這只是一個像素覆蓋另一個像素簡單的一步計算,而正常情況我們現實的界面會有非常多的層,每一層都會有百萬計的像素,這都要GPU去計算,負擔是很重的。

像素對齊

像素對齊就是視圖上像素和屏幕上的物理像素完美對齊。上面我們說混合的時候,假設的情況是多個layer是在每個像素都完全對齊的情況下來進行計算的,如果像素不對齊的情況下,GPU需要進行Anti-aliasing反抗鋸齒計算,GPU的負擔就會加重。像素對齊的情況下,我們只需要把所有layer上的單個像素進行混合計算即可。

那么什么原因造成像素不對齊?主要有兩點:

  1. 圖片大小和UIImageView大小不符合2倍3倍關系時,如一張12x12二倍,18x18三倍的圖,UIimageView的size為6x6才符合像素對齊。

  2. 邊緣像素不對齊,即起始坐標不是整數,可以使用CGRectIntegral()方法去除小數位。 這兩點都有可能造成像素不對齊。如果想獲得更好的圖形性能,作為開發者要盡可能得避免這兩種情況。

不透明

上面我們說過一個混合計算的公式:

R = S + D * ( 1 – Sa )

如果Sa值為1,也就是源色對應的像素不透明。那么得到R = S,這樣就只需要拷貝最上層的layer,不需要再進行復雜的計算了。因為下面層的layer全部是可不見的,所以GPU無需進行混合計算了。

如何讓GPU知道這個圖像是不透明的呢?如果使用的是CALayer,那么要把opaque屬性設置成YES(默認是NO)。而若只用的是UIView,opaque默認屬性是YES。當GPU知道是不透明的時候,只會做簡單的拷貝工作,避免了復雜的計算,大大減輕了GPU的工作量。

如果加載一個沒有alpha通道的圖片,opaque屬性會自動設置為YES。但是如果是一個每個像素alpha值都為100%的圖片,盡管此圖不透明但是Core Animation依然會假定是否存在alpha值不為100%的像素。

解碼

上一篇文章我們有說到,一般在Core Animation準備階段,會對圖片進行解碼操作,即把壓縮的圖像解碼成位圖數據。這是一個很消耗CPU的事情。系統是在圖片將要渲染到屏幕之前再進行解碼,而且默認是在主線程中進行的。所以我們可以將解碼放在子線程中進行,下面簡單列舉一種解碼方式:

NSString *picPath = [[NSBundle mainBundle] pathForResource:@"tests" ofType:@"png"];
NSData *imageData = [NSData dataWithContentsOfFile:picPath];//讀取未解碼圖片數據

CGImageSourceRef imageSourceRef = CGImageSourceCreateWithData((__bridge CFTypeRef)imageData, NULL);
CGImageRef imageRef = CGImageSourceCreateImageAtIndex(imageSourceRef, 0, (CFDictionaryRef)@{(id)kCGImageSourceShouldCache:@(NO)});
CFRelease(imageSourceRef);
size_t width = CGImageGetWidth(imageRef);//獲取圖片寬度
size_t height = CGImageGetHeight(imageRef);//獲取圖片高度
CGColorSpaceRef colorSpace = CGImageGetColorSpace(imageRef);

size_t bitsPerComponent = CGImageGetBitsPerComponent(imageRef);//每個顏色組件占的bit數
size_t bitsPerPixel = CGImageGetBitsPerPixel(imageRef);//每個像素占幾bit
size_t bytesPerRow = CGImageGetBytesPerRow(imageRef);//位圖數據每行占多少bit
CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef);

CGDataProviderRef dataProvider = CGImageGetDataProvider(imageRef);
CFRelease(imageRef);
CFDataRef dataRef = CGDataProviderCopyData(dataProvider);//獲得解碼后數據
CGDataProviderRef newProvider = CGDataProviderCreateWithCFData(dataRef);
CFRelease(dataRef);

CGImageRef newImageRef = CGImageCreate(width, height, bitsPerComponent, bitsPerPixel, bytesPerRow, colorSpace, bitmapInfo, newProvider, NULL, false, kCGRenderingIntentDefault);
CFRelease(newProvider);

UIImage *image = [UIImage imageWithCGImage:newImageRef scale:2.0 orientation:UIImageOrientationUp];
CFRelease(newImageRef);

另外,在iOS7之后蘋果提供了一個屬性kCGImageSourceShouldCacheImmediately,在CGImageSourceCreateImageAtIndex方法中,設置kCGImageSourceShouldCacheImmediately為kCFBooleanTrue的話可以立刻開始解壓縮,默認為kCFBooleanFalse。

當然也像AFNetworking 中使用void CGContextDrawImage(CGContextRef __nullable c, CGRect rect, CGImageRef __nullable image)方法也可以實現解碼,具體實現不在此贅述。

字節對齊

我們前面說像素對齊時,簡單介紹了字節對齊。那么到底什么是字節對齊?為什么要字節對齊?和我們優化圖形性能有什么關系呢?

字節對齊是對基本數據類型的地址做了一些限制,即某種數據類型對象的地址必須是其值的整數倍。例如,處理器從內存中讀取一個8個字節的數據,那么數據地址必須是8的整數倍。

對齊是為了提高讀取的性能。因為處理器讀取內存中的數據不是一個一個字節讀取的,而是一塊一塊讀取的一般叫做cache lines。如果一個不對齊的數據放在了2個數據塊中,那么處理器可能要執行兩次內存訪問。當這種不對齊的數據非常多的時候,就會影響到讀取性能了。這樣可能會犧牲一些儲存空間,但是對提升了內存的性能,對現代計算機來說是更好的選擇。

在iOS中,如果這個圖像的數據沒有字節對齊,那么Core Animation會自動拷貝一份數據做對齊處理。這里我們可以提前做好字節對齊。

在方法CGBitmapContextCreate(void * __nullable data, size_t width, size_t height, size_t bitsPerComponent, size_t bytesPerRow, CGColorSpaceRef __nullable space, uint32_t bitmapInfo)中,有一個參數bytesPerRow,意思是指定要使用的位圖每行內存的字節數,ARMv7架構的處理器的cache lines是32byte,A9處理器的是64byte,這里我們要使bytesPerRow為64的整數倍。

具體可以參考官方文檔Quartz 2D Programming Guide和WWDC 2012 Session 238 "iOS App Performance: Graphics and Animations"。字節對齊,在一般情況下,感覺對性能的影響很小,沒必要的情況不要過早優化。

離屏渲染

離屏渲染(Off-Screen Rendering)是指GPU在當前屏幕緩沖區以外新開辟一個緩沖區進行渲染操作。離屏渲染是很消耗性能的,因為首先要創建屏幕外緩沖區,還要進行兩次上下文環境切換。先切換到屏幕外環境,離屏渲染完成后再切換到當前屏幕,上下文的切換是很高昂的消耗。產生離屏渲染的原因就是這些圖層不能直接繪制在屏幕上,必須進行預合成。

產生離屏渲染的情況大概有幾種: 

  1. cornerRadius和masksToBounds(UIView中是clipToBounds)一起使用的時候,單獨使用不會觸發離屏渲染。cornerRadius只對背景色起作用,所以有contents的圖層需要對其進行裁剪。

  2. 為圖層設置mask(遮罩)。

  3. layer的allowsGroupOpacity屬性為YES且opacity小于1.0,GroupOpacity是指子圖層的透明度值不能大于父圖層的。 

  4. 設置了shadow(陰影)。

上面這幾種情況都是GPU的離屏渲染,還有一種特殊的CPU離屏渲染。只要實現Core Graphics繪制API會產生CPU的離屏渲染。因為它也不是直接繪制到屏幕上的,而且先創建屏幕外的緩存。

我們如何解決這幾個產生離屏渲染的問題呢?首先,GroupOpacity對性能幾乎沒有影響,在此就不多說了。圓角是一個無法避免的,網上有很多例子是用Core Graphics繪制來代替系統圓角的,但是Core Graphics是一種軟件繪制,利用的是CPU,性能上要差上不少

當然在CPU利用率不是很高的界面是個不錯的選擇,但是有時候某個界面可能需要CPU去做其他消耗很大的事情,如網絡請求。這個時候時候在用Core Graphics繪制大量的圓角圖形就有可能出現掉幀。

這種情況怎么辦呢?最好的就是設計師直接提供圓角圖像。還有一種折中的方法就是在混合圖層,在原圖層上覆蓋一個你要的圓角形狀的圖層,中間需要顯示的部分是透明的,覆蓋的部分和周圍背景一致。

對于shadow,如果圖層是個簡單的幾何圖形或者圓角圖形,我們可以通過設置shadowPath來優化性能,能大幅提高性能。示例如下:

imageView.layer.shadowColor = [UIColor grayColor].CGColor;
imageView.layer.shadowOpacity = 1.0;
imageView.layer.shadowRadius = 2.0;
UIBezierPath *path = [UIBezierPath bezierPathWithRect:imageView.frame];
imageView.layer.shadowPath = path.CGPath;

我們還可以通過設置shouldRasterize屬性值為YES來強制開啟離屏渲染。其實就是光柵化(Rasterization)。

既然離屏渲染這么不好,為什么我們還要強制開啟呢?當一個圖像混合了多個圖層,每次移動時,每一幀都要重新合成這些圖層,十分消耗性能。

當我們開啟光柵化后,會在首次產生一個位圖緩存,當再次使用時候就會復用這個緩存。但是如果圖層發生改變的時候就會重新產生位圖緩存。

所以這個功能一般不能用于UITableViewCell中,cell的復用反而降低了性能。最好用于圖層較多的靜態內容的圖形。而且產生的位圖緩存的大小是有限制的,一般是2.5個屏幕尺寸。在100ms之內不使用這個緩存,緩存也會被刪除。所以我們要根據使用場景而定。

Instruments

上面我們說了這么多性能相關的因素,那么我們怎么進行性能的測試,怎么知道哪些因素影響了圖形性能?蘋果很人性得為我們提供了一個測試工具Instruments。可以在Xcode->Open Develeper Tools->Instruments中找到,我們看到這里面有很多的測試工具,像大家可能常用的檢測內存泄漏的Leaks,在這里我們就討論下Core Animation這個工具的使用。

Core Animation工具用來監測Core Animation性能。提供可見的FPS值。并且提供幾個選項來測量渲染性能,下面我們來說明每個選項的能: 

  • Color Blended Layers:這個選項如果勾選,你能看到哪個layer是透明的,GPU正在做混合計算。顯示紅色的就是透明的,綠色就是不透明的。

  • Color Hits Green and Misses Red:如果勾選這個選項,且當我們代碼中有設置shouldRasterize為YES,那么紅色代表沒有復用離屏渲染的緩存,綠色則表示復用了緩存。我們當然希望能夠復用。

  • Color Copied Images:按照官方的說法,當圖片的顏色格式GPU不支持的時候,即不是32bit的顏色格式,Core Animation會 拷貝一份數據讓CPU進行轉化。例如從網絡上下載了8bit的顏色格式的圖片,則需要CPU進行轉化,這個區域會顯示成藍色。還有一種情況會觸發Core Animation的copy方法,就是字節不對齊的時候。

  • Color Misaligned Images:勾選此項,如果圖片需要縮放則標記為黃色,如果沒有像素對齊則標記為紫色。像素對齊我們已經在上面有所介紹。

  • Color Offscreen-Rendered Yellow:用來檢測離屏渲染的,如果顯示黃色,表示有離屏渲染。當然還要結合Color Hits Green and Misses Red來看,是否復用了緩存。

  • Color OpenGL Fast Path Blue:這個選項對那些使用OpenGL的圖層才有用,像是GLKView或者 CAEAGLLayer,如果不顯示藍色則表示使用了CPU渲染,繪制在了屏幕外,顯示藍色表示正常。

  • Flash Updated Regions:當對圖層重繪的時候回顯示黃色,如果頻繁發生則會影響性能。可以用增加緩存來增強性能。官方文檔Improving Drawing Performance(https://developer.apple.com/library/archive/documentation/2DDrawing/Conceptual/DrawingPrintingiOS/DrawingTips/DrawingTips.html)有所說明。

關于“iOS如何實現圖形性能優化”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,使各位可以學到更多知識,如果覺得文章不錯,請把它分享出去讓更多的人看到。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

ios
AI

秀山| 儋州市| 梨树县| 宁海县| 洛浦县| 沙坪坝区| 玉龙| 襄汾县| 漯河市| 蒲城县| 吉水县| 西乌| 江都市| 陆川县| 汽车| 巍山| 东乌珠穆沁旗| 尼玛县| 邳州市| 舞钢市| 安顺市| 汶川县| 大邑县| 内乡县| 莲花县| 孝义市| 云龙县| 得荣县| 马尔康县| 岳阳县| 临城县| 社会| 毕节市| 通化县| 攀枝花市| 黄梅县| 洮南市| 新乐市| 永安市| 台江县| 曲沃县|