天天看點

iOS開發面試隻需知道這些,技術基本通關!(性能優化篇)

iOS開發面試隻需知道這些,技術基本通關!(性能優化篇)

在性能優化中一個最具參考價值的屬性是<code>FPS</code>:<code>FramesPerSecond</code>,其實就是螢幕重新整理率,蘋果的<code>iphone</code>推薦的重新整理率是60Hz,也就是說<code>GPU</code>每秒鐘重新整理螢幕60次,這每重新整理一次就是一幀<code>frame</code>,<code>FPS</code>也就是每秒鐘重新整理多少幀畫面。靜止不變的頁面<code>FPS</code>值是0,這個值是沒有參考意義的,隻有當頁面在執行動畫或者滑動的時候,<code>FPS</code>值才具有參考價值,<code>FPS</code>值的大小展現了頁面的流暢程度高低,當低于45的時候卡頓會比較明顯。

圖層混合:

每一個 <code>layer</code>是一個紋理,所有的紋理都以某種方式堆疊在彼此的頂部。對于螢幕上的每一個像素,<code>GPU</code>需要算出怎麼混合這些紋理來得到像素 <code>RGB</code>的值。

當<code>Sa</code>= 0.5時,<code>RGB</code>值為(0.5,  0, 0),可以看出,當兩個不是完全不透明的 <code>CALayer</code>覆寫在一起時,<code>GPU</code>大量做這種複合操作,随着這中操作的越多,GPU越忙碌,性能肯定會受到影響。

公式:

R = S + D * ( 1 – Sa )

結果的顔色是源色彩(頂端紋理)+目标顔色(低一層的紋理)*(1-源顔色的透明度)。

當<code>Sa</code>= 1時,<code>R = S</code>,<code>GPU</code>将不會做任何合成,而是簡單從這個層拷貝,不需要考慮它下方的任何東西(因為都被它遮擋住了),這節省了 <code>GPU</code>相當大的工作量。

1、用<code>ARC</code>管理記憶體

2、在正确的地方使用 <code> reuseIdentifier</code>

3、盡量把<code>views</code>設定為透明

4、避免過于龐大的  <code>XIB</code>

5、不要阻塞主線程

6、在<code>ImageViews</code>中調整圖檔大小。如果要在 <code>UIImageView</code>中顯示一個來自  bundle的圖檔,你應保證圖檔的大小和 <code>UIImageView</code>的大小相同。在運作中縮放圖檔是很耗費資源的,特别是 <code>UIImageView</code>嵌套在

<code>UIScrollView</code>中的情況下。如果圖檔是從遠端服務加載的你不能控制圖檔大小,比如在下載下傳前調整到合适大

小的話,你可以在下載下傳完成後,最好是用  <code>backgroundthread</code>,縮放一次,然後在 <code>UIImageView</code>中使用縮放後的圖檔。

7、選擇正确的<code>Collection</code>。

<code>Arrays</code>:有序的一組值。使用 <code> index</code>來 <code> lookup</code>很快,使用 <code> value lookup</code>很慢,插入/删除很慢。

<code>Dictionaries</code>:存儲鍵值對。用鍵來查找比較快。

<code>Sets</code>:無序的一組值。用值來查找很快,插入/删除很快。

8、打開<code>gzip</code>壓縮。<code>app</code>可能大量依賴于伺服器資源,問題是我們的目标是移動裝置,是以你就不能指望網

絡狀況有多好。減小文檔的一個方式就是在服務端和你的 app中打開 gzip。這對于文字這種能有更高壓縮率的資料來說會有更顯著的效用。

<code>iOS</code>已經在 <code> NSURLConnection</code>中預設支援了<code>gzip</code>壓縮,當然 <code> AFNetworking</code>這些基于它的架構亦然。

1、重用和延遲加載<code>(lazy load) Views</code>

更多的  <code>view</code>意味着更多的渲染,也就是更多的<code> CPU</code>和記憶體消耗,對于那種嵌套了很多<code>view</code>在

<code>UIScrollView</code>裡邊的  <code>app</code>更是如此。

這裡我們用到的技巧就是模仿<code>UITableView</code>和 <code>UICollectionView</code>的操作:不要一次建立所有的 <code>subview</code>,

而是當需要時才建立,當它們完成了使命,把他們放進一個可重用的隊列中。這樣的話你就隻需要在滾動發生時建立你的 <code>views</code>,避免了不劃算的記憶體配置設定。

2、<code>Cache</code>, <code>Cache</code>,還是  <code> Cache</code>!

一個極好的原則就是,緩存所需要的,也就是那些不大可能改變但是需要經常讀取的東西。

我們能緩存些什麼呢?一些選項是,遠端伺服器的響應,圖檔,甚至計算結果,比如<code>UITableView</code>的行高。

<code>NSCache</code>和 <code> NSDictionary</code>類似,不同的是系統回收記憶體的時候它會自動删掉它的内容。

3、權衡渲染方法.性能能還是要 <code>bundle</code>保持合适的大小。

4、處理記憶體警告.移除對緩存,圖檔 <code>object</code>和其他一些可以重建立的 <code>objects</code>的  <code>strong references</code>.

5、重用大開銷對象

6、一些 <code>objects</code>的初始化很慢,比如 <code>NSDateFormatter</code>和 <code> NSCalendar</code>。然而,你又不可避免地需要使用它們,比如從 <code>JSON</code>或者 <code>XML</code>中解析資料。想要避免使用這個對象的瓶頸你就需要重用他們,可以通過添加屬性到你的 <code>class</code>裡或者建立靜态變量來實作。

7、避免反複處理資料.在伺服器端和用戶端使用相同的資料結構很重要。

8、選擇正确的資料格式.解析 <code>JSON</code>會比<code>XML</code>更快一些,<code>JSON</code>也通常更小更便于傳輸。從<code> iOS5</code>起有了官方内建的 <code>JSON deserialization</code>就更加友善使用了。但是  <code>XML</code>也有<code> XML</code>的好處,比如使用 <code> SAX</code>來解析<code> XML</code>就像解析本地檔案一樣,你不需像解析 <code>json</code>一樣等到整個文檔下載下傳完成才開始解析。當你處理很大的資料的時候就會極大地減低記憶體消耗和增加性能。

9、正确設定背景圖檔

全屏背景圖,在<code>view</code>中添加一個 <code>UIImageView</code>作為一個子   <code>View</code>

隻是某個小的 <code>view</code>的背景圖,你就需要用 <code> UIColor</code>的 <code>colorWithPatternImage</code>來做了,它會更快地渲染

也不會花費很多記憶體。

10、減少使用 <code>Web</code>特性。想要更高的性能你就要調整下你的<code>HTML</code>了。第一件要做的事就是盡可能移除不必要的<code>javascript</code>,避免使用過大的架構。能隻用原生 js就更好了。盡可能異步加載例如使用者行為統計 <code> script</code>這種不影響頁面表達的 <code>javascript</code>。注意你使用的圖檔,保證圖檔的符合你使用的大小。

11、<code>Shadow Path</code>。<code>CoreAnimation</code>不得不先在背景得出你的圖形并加好陰影然後才渲染,這開銷是很大的。使用 <code>shadowPath</code>的話就避免了這個問題。使用 <code>shadow path</code>的話  iOS就不必每次都計算如何渲染,它使用一個預先計算好的路徑。但問題是自己計算 <code>path</code>的話可能在某些 <code>View</code>中比較困難,且每當<code> view</code>的<code>  frame</code>變化的時候你都需要去 <code>update shadow path</code>.

12、優化 <code>Table View</code>

正确使用<code>reuseIdentifier</code>來重用 <code> cells</code>

盡量使所有的<code>view opaque</code>,包括 <code>cell</code>自身

避免漸變,圖檔縮放,背景選人

緩存行高

如果 <code>cell</code>内現實的内容來自<code>web</code>,使用異步加載,緩存請求結果

使用 <code>shadowPath</code>來畫陰影

減少 <code>subviews</code>的數量

盡量不适用<code>cellForRowAtIndexPath</code>:,如果你需要用到它,隻用-一次然後緩存結果

使用正确的資料結構來存儲資料

使用<code>rowHeight</code>, <code>sectionFooterHeight</code>和  <code> sectionHeaderHeight</code>來設定固定的高,不要請求  <code> delegate</code>

13、選擇正确的資料存儲選項

<code>NSUserDefaults</code>的問題是什麼?雖然它很  <code>nice</code>也很便捷,但是它隻适用于小資料,比如一些簡單的布爾型的設定選項,再大點你就要考慮其它方式了

<code>XML</code>這種結構化檔案呢?總體來說,你需要讀取整個檔案到記憶體裡去解析,這樣是很不經濟的。使用<code>SAX</code>又是一個很麻煩的事情。

<code>NSCoding</code>?不幸的是,它也需要讀寫檔案,是以也有以上問題。

在這種應用場景下,使用 <code>SQLite</code>或者   <code>Core Data</code>比較好。使用這些技術你用特定的查詢語句就能隻加載你需要的對象。

在性能層面來講,<code>SQLite</code>和  <code>Core Data</code>是很相似的。他們的不同在于具體使用方法。

<code>Core Data</code>代表一個對象的 <code>graph model</code>,但 <code>SQLite</code>就是一個  <code>DBMS</code>。

<code>Apple</code>在一般情況下建議使用 <code> Core Data</code>,但是如果你有理由不使用它,那麼就去使用更加底層的 <code> SQLite</code>吧。

如果你使用 <code>SQLite</code>,你可以用 <code>FMDB</code>這個庫來簡化<code>SQLite</code>的操作,這樣你就不用花很多經曆了解  <code> SQLite</code>的 <code>C API</code>了。

首先作為一個開發者,有一個學習的氛圍跟一個交流圈子特别重要,這是一個我的ioser公衆号:程式設計大鑫,不管你是小白還是大牛都歡迎入駐 ,讓我們一起進步,共同發展!(會免費提供一些群主收藏的免費學習書籍資料以及整理好的幾百道面試題和答案文檔!)

1、加速啟動時間。快速打開<code>app</code>是很重要的,特别是使用者第一次打開它時,對 <code>app</code>來講,第一印象太太太重要了。你能做的就是使它盡可能做更多的異步任務,比如加載遠端或者資料庫資料,解析資料。避免過于龐大的<code>XIB</code>,因為他們是在主線程上加載的。是以盡量使用沒有這個問題的 <code>Storyboards</code>吧!一定要把裝置從<code>Xcode</code>斷開來測試啟動速度

2、使用 <code>Autorelease Pool</code>。<code>NSAutoreleasePool</code>負責釋放 <code>block</code>中的 <code>autoreleased objects</code>。一般情況下它會自動被<code>UIKit</code>調用。但是有些狀況下你也需要手動去建立它。假如你建立很多臨時對象,你會發現記憶體一直在減少直到這些對象被<code>release</code>的時候。這是因為隻有當<code>UIKit</code>用光了  <code>autorelease pool</code>的時候 <code> memory</code>才會被釋放。消息是你可以在你自己的<code>@autoreleasepool</code>裡建立臨時的對象來避免這個行為。

3、選擇是否緩存圖檔。常見的從 <code>bundle</code>中加載圖檔的方式有兩種,一個是用<code>imageNamed</code>,二是用<code>imageWithContentsOfFile</code>,第一種比較常見一點。

4、避免日期格式轉換。如果你要用 <code>NSDateFormatter</code>來處理很多日期格式,應該小心以待。就像先前提到的,任何時候重用<code>NSDateFormatters</code>都是一個好的實踐。如果你可以控制你所處理的日期格式,盡量選擇Unix時間戳。你可以友善地從時間戳轉換到  <code> NSDate</code>:

iOS開發面試隻需知道這些,技術基本通關!(性能優化篇)

這樣會比用 C來解析日期字元串還快!需要注意的是,許多<code>web API</code>會以微秒的形式傳回時間戳,因為這種格式在 <code>javascript</code>中更友善使用。記住用<code>dateFromUnixTimestamp</code>之前除以  1000就好了。

平時你是如何對代碼進行性能優化的?

利用性能分析工具檢測,包括靜态<code> Analyze</code>工具,以及運作時  <code> Profile</code>工具,通過  <code> Xcode</code>工具欄中<code>Product-&amp;gt;Profile</code>可以啟動,比如測試程式啟動運作時間,當點選 <code>Time Profiler</code>應用程式開始運作後.就能擷取到整個應用程式運作消耗時間分布和百分比.為了保證資料分析在統一使用場景真實需要注意一定要使用真機,因為此時模拟器是運作在 <code>Mac</code>上,而 <code>Mac</code>上的  <code>CPU</code>往往比 <code> iOS</code>裝置要快。

為了防止一個應用占用過多的系統資源,開發<code>iOS</code>的蘋果工程師門設計了一個“看門狗”的機制。在不同的場景下,“看門狗”會監測應用的性能。如果超出了該場景所規定的運作時間,“看門狗”就會強制終結這個應用的程序。開發者們在<code>crashlog</code>裡面,會看到諸如 <code>0x8badf00d</code>這樣的錯誤代碼。

優化 <code>Table View</code>

盡量使所有的 <code>view opaque</code>,包括<code>cell</code>自身

如果 <code>cell</code>内現實的内容來自 <code>web</code>,使用異步加載,緩存請求結果減少<code>subviews</code>的數量

盡量不适用<code>cellForRowAtIndexPath</code>:,如果你需要用到它,隻用一次然後緩存結果

使用<code>rowHeight</code>, <code>sectionFooterHeight</code>和<code>sectionHeaderHeight</code>來設定固定的高,不要請求   <code>delegate</code>

<code>UIImage</code>加載圖檔性能問題

<code>imagedNamed</code>初始化

<code>imageWithContentsOfFile</code>初始化

*<code>imageNamed</code>預設加載圖檔成功後會記憶體中緩存圖檔,這個方法用一個指定的名字在系統緩存中查找并傳回一個圖檔對象.如果緩存中沒有找到相應的圖檔對象,則從指定地方加載圖檔然後緩存對象,并傳回這個圖檔對象.

<code>imageWithContentsOfFile</code>則僅隻加載圖檔,不緩存.

加載一張大圖并且使用一次,用 imageWithContentsOfFile是最好,這樣 CPU不需要做緩存節約時間.

使用場景需要程式設計時,應該根據實際應用場景加以區分,UIimage雖小,但使用元素較多問題會有所凸顯.

不要在 <code>viewWillAppear</code>中做費時的操作:<code>viewWillAppear</code>:在    <code>view</code>顯示之前被調用,出于效率考慮,方法中不要處理複雜費時操作;在該方法設定  <code>view</code>的顯示屬性之類的簡單事情,比如背景色,字型等。否則,會明顯感覺到  <code>view</code>有卡頓或者延遲。

在正确的地方使用 <code>reuseIdentifier</code>:<code>table view</code>用  <code>tableView</code>:<code>cellForRowAtIndexPath</code>:為 <code>rows</code>配置設定

<code>cells</code>的時候,它的資料應該重用自  <code>UITableViewCell</code>。

盡量把<code>views</code>設定為透明:如果你有透明的 <code>Views</code>你應該設定它們的  <code>opaque</code>屬性為  <code>YES</code>。系統用一個最優的方式渲染這些 <code>views</code>。這個簡單的屬性在 <code>IB</code>或者代碼裡都可以設定。

避免過于龐大的<code>XIB</code>:盡量簡單的為每個<code>Controller</code>配置一個單獨的<code>XIB</code>,盡可能把一個 <code> ViewController</code>的  <code>view</code>層次結構分散到單獨的<code>XIB</code>中去,當你加載一個引用了圖檔或者聲音資源的  <code>  nib</code>時,<code>nib</code>加載代碼會把圖檔和聲音檔案寫進記憶體。

不要阻塞主線程:永遠不要使主線程承擔過多。因為 <code>UIKit</code>在主線程上做所有工作,渲染,管理觸摸反應,回應輸入等都需要在它上面完成,大部分阻礙主程序的情形是你的 <code>app</code>在做一些牽涉到讀寫外部資源的 I/O操作,比如存儲或者網絡。

iOS開發面試隻需知道這些,技術基本通關!(性能優化篇)

在 <code>Image Views</code>中調整圖檔大小

如果要在 UIImageView中顯示一個來自 bundle的圖檔,你應保證圖檔的大小和  UIImageView的大小相同。在運作中縮放圖檔是很耗費資源的.

講講你用<code>Instrument</code>優化動畫性能的經曆吧(别問我什麼是  <code>Instrument</code>)

iOS開發面試隻需知道這些,技術基本通關!(性能優化篇)

facebook啟動時間優化

1.瘦身請求依賴

2.<code>UDP</code>啟動請求先行緩存

3.隊列串行化處理啟動響應

光栅化是将幾何資料經過一系列變換後最終轉換為像素,進而呈現在顯示裝置上的過程,光栅化的本質是坐标變換、幾何離散化我們使用  UITableView和   UICollectionView時經常會遇到各個   Cell的樣式是一樣的,這時候我們可以使用這個屬性提高性能:

iOS開發面試隻需知道這些,技術基本通關!(性能優化篇)

目前我知道的方式有以下幾種

<code>Memory Leaks</code>

<code>Alloctions</code>

<code>Analyse</code>

<code>Debug Memory Graph</code>

<code>MLeaksFinder</code>

洩露的記憶體主要有以下兩種:

<code>Lak  Memory</code>這種是忘記   <code>Release</code>操作所洩露的記憶體。

<code>Abandon  Memory</code>這種是循環引用,無法釋放掉的記憶體。

上面所說的五種方式,其實前四種都比較麻煩,需要不斷地調試運作,第五種是騰訊閱讀團隊出品,效果好一些

視圖和圓角的大小對幀率并沒有什麼卵影響,數量才是傷害的核心輸出

iOS開發面試隻需知道這些,技術基本通關!(性能優化篇)

首先上面的方式是不可取的,會觸發離屏渲染。

如果能夠隻用  <code>cornerRadius</code>解決問題,就不用優化。

如果必須設定  <code>masksToBounds</code>,可以參考圓角視圖的數量,如果數量較少(一頁隻有幾個)也可以考慮不用優化。

<code>UIImageView</code>的圓角通過直接截取圖檔實作,其它視圖的圓角可以通過   <code>Core  Graphics</code>畫出圓角矩形實作。

iOS開發面試隻需知道這些,技術基本通關!(性能優化篇)

本質上是降低<code> CPU</code>、<code>GPU</code>的工作,從這兩個大的方面去提升性能。

<code>CPU</code>:對象的建立和銷毀、對象屬性的調整、布局計算、文本的計算和排版、圖檔的格式轉換和解碼、圖像的繪制

<code>GPU</code>:紋理的渲染

卡頓優化在CPU層面

盡量用輕量級的對象,比如用不到事件處理的地方,可以考慮使用  <code>CALayer</code>取代   <code>UIView</code>

不要頻繁地調用 <code> UIView</code>的相關屬性,比如    <code>frame</code>、<code>bounds</code>、<code>transform</code>等屬性,盡量減少不必要的修改

盡量提前計算好布局,在有需要時一次性調整對應的屬性,不要多次修改屬性

<code>Autolayout</code>會比直接設定   <code>frame</code>消耗更多的    <code>CPU</code>資源

圖檔的 <code> size</code>最好剛好跟  <code> UIImageView</code>的   <code>size</code>保持一緻

控制一下線程的最大并發數量

盡量把耗時的操作放到子線程

文本處理(尺寸計算、繪制)

圖檔處理(解碼、繪制)

卡頓優化在GPU層面

盡量避免短時間内大量圖檔的顯示,盡可能将多張圖檔合成一張進行顯示

<code>GPU</code>能處理的最大紋理尺寸是   4096x4096,一旦超過這個尺寸,就會占用  <code>CPU</code>資源進行處理,是以紋理盡量不要超過這個尺寸

盡量減少視圖數量和層次

減少透明的視圖(<code>alpha</code>&lt;1),不透明的就設定  <code>opaque</code>為   <code>YES</code>

盡量避免出現離屏渲染

1.預排版,提前計算

在接收到服務端傳回的資料後,盡量将  <code>CoreText</code>排版的結果、單個控件的高度、<code>cell</code>整體的高度提前計算好,将其存儲在模型的屬性中。需要使用時,直接從模型中往外取,避免了計算的過程。盡量少用 <code> UILabel</code>,可以使用 <code>CALayer</code>。避免使用 <code>   AutoLayout</code>的自動布局技術,采取純代碼的方式

2.預渲染,提前繪制

例如圓形的圖示可以提前在,在接收到網絡傳回資料時,在背景線程進行處理,直接存儲在模型資料裡,回到主線程後直接調用就可以了避免使用 <code> CALayer</code>的   <code>Border</code>、<code>corner</code>、<code>shadow</code>、<code>mask</code>等技術,這些都會觸發離屏渲染。

3.異步繪制

4.全局并發線程

5.高效的圖檔異步加載

程式的耗電主要在以下四個方面:

·CPU處理

·定位

·網絡

·圖像

優化的途徑主要展現在以下幾個方面:

·盡可能降低  <code>CPU</code>、<code>GPU</code>的功耗。

·盡量少用定時器。

·優化<code> I/O</code>操作。

o 不要頻繁寫入小資料,而是積攢到一定數量再寫入

o 讀寫大量的資料可以使用 <code> Dispatch_io</code>,<code>GCD</code>内部已經做了優化。

o 資料量比較大時,建議使用資料庫

網絡方面的優化

o 減少壓縮網絡資料(<code>XML  -&amp;gt; JSON -&amp;gt; ProtoBuf</code>),如果可能建議使用 <code>ProtoBuf</code>。

o 如果請求的傳回資料相同,可以使用 <code> NSCache</code>進行緩存

o 使用斷點續傳,避免因網絡失敗後要重新下載下傳。

o 網絡不可用的時候,不嘗試進行網絡請求

o 長時間的網絡請求,要提供可以取消的操作

o 采取批量傳輸。下載下傳視訊流的時候,盡量一大塊一大塊的進行下載下傳,廣告可以一次下載下傳多個

定位層面的優化

o 如果隻是需要快速确定使用者位置,最好用  <code>CLLocationManager</code>的 <code>  requestLocation</code>方法。定位完成後,會自動讓定位硬體斷電

o 如果不是導航應用,盡量不要實時更新位置,定位完畢就關掉定位服務

o 盡量降低定位精度,比如盡量不要使用精度最高的  <code>kCLLocationAccuracyBest</code>

o 需要背景定位時,盡量設定  <code>pausesLocationUpdatesAutomatically</code>為   <code>YES</code>,如果使用者不太可能移動的時候系統會自動暫停位置更新

o 盡量不要使用<code>startMonitoringSignificantLocationChanges</code>,優先考慮<code>startMonitoringForRegion</code>:

硬體檢測優化

o 使用者移動、搖晃、傾斜裝置時,會産生動作(motion)事件,這些事件由加速度計、陀螺儀、磁力計等硬體檢測。在不需要檢測的場合,應該及時關閉這些硬體

降低包大小需要從兩方面着手

可執行檔案

編譯器優化

<code>Strip Linked Product</code>、<code>Make Strings Read-Only</code>、<code>Symbols Hidden by Default</code>設定為  <code>YES</code>

去掉異常支援,<code>Enable C++ Exceptions</code>、<code>Enable Objective-C Exceptions</code>設定為  <code> NO</code>, <code>Other C Flags</code>添加  <code>-fno-exceptions</code>

利用 <code> AppCode</code>檢測未使用的代碼:菜單欄 <code>  -&amp;gt; Code -&amp;gt; Inspect Code</code>

編寫 <code>LLVM</code>插件檢測出重複代碼、未被調用的代碼

資源

資源包括圖檔、音頻、視訊等

優化的方式可以對資源進行無損的壓縮

去除沒有用到的資源

離屏渲染就是在目前螢幕緩沖區以外,新開辟一個緩沖區進行操作。

離屏渲染出發的場景有以下:

圓角(<code>maskToBounds</code>并用才會觸發)

圖層蒙版

陰影

光栅化

為什麼要避免離屏渲染?

<code>CPU GPU</code>在繪制渲染視圖時做了大量的工作。離屏渲染發生在  <code> GPU</code>層面上,會建立新的渲染緩沖區,會

觸發<code> OpenGL</code>的多通道渲染管線,圖形上下文的切換會造成額外的開銷,增加   <code>GPU</code>工作量。如果  <code> CPU GPU</code>累計耗時   16.67毫秒還沒有完成,就會造成卡頓掉幀。

圓角屬性、蒙層遮罩都會觸發離屏渲染。指定了以上屬性,标記了它在新的圖形上下文中,在未愈合之前,不可以用于顯示的時候就出發了離屏渲染。

在 <code>OpenGL</code>中,<code>GPU</code>有  2種渲染方式

<code>On-Screen Rendering</code>:目前螢幕渲染,在目前用于顯示的螢幕緩沖區進行渲染操作

<code>Off-Screen Rendering</code>:離屏渲染,在目前螢幕緩沖區以外新開辟一個緩沖區進行渲染操作

離屏渲染消耗性能的原因

需要建立新的緩沖區

離屏渲染的整個過程,需要多次切換上下文環境,先是從目前螢幕( <code>  On-Screen</code>)切換到離屏(<code>Off-Screen</code>);等到離屏渲染結束以後,将離屏緩沖區的渲染結果顯示到螢幕上,又需要将上下文環境從離屏切換到目前螢幕

哪些操作會觸發離屏渲染?

光栅化,<code>layer.shouldRasterize = YES</code>

遮罩,<code>layer.mask</code>

圓角,同時設定  <code>layer.masksToBounds = YES、layer.cornerRadius</code>大于  0

考慮通過  <code>CoreGraphics</code>繪制裁剪圓角,或者叫美工提供圓角圖檔

陰影,<code>layer.shadowXXX</code>,如果設定了  <code>layer.shadowPath</code>就不會産生離屏渲染

1、模拟器<code>debug-</code>選中 <code>color Offscreen - Renderd</code>離屏渲染的圖層高亮成黃可能存在性能問題

2、真機<code>Instrument-</code>選中<code>Core Animation-</code>勾選<code>Color Offscreen-Rendered Yellow</code>

離屏渲染的觸發方式

設定了以下屬性時,都會觸發離屏繪制:

1、<code>layer.shouldRasterize</code>(光栅化)

光栅化概念:将圖轉化為一個個栅格組成的圖象。

光栅化特點:每個元素對應幀緩沖區中的一像素。

2、<code>masks</code>(遮罩)

3、<code>shadows</code>(陰影)

4、<code>edge antialiasing</code>(抗鋸齒)

5、<code>group opacity</code>(不透明)

6、複雜形狀設定圓角等

7、漸變

8、<code>drawRect</code>

例如我們日程經常打交道的 <code>TableViewCell</code>,因為<code>TableViewCell</code>的重繪是很頻繁的(因為 <code>Cell</code>的複用),如果<code>Cell</code>的内容不斷變化,則  <code>Cell</code>需要不斷重繪,如果此時設定了 <code>cell.layer</code>可光栅化。則會造成大量的離屏渲染,降低圖形性能。

如果将不在<code>GPU</code>的目前螢幕緩沖區中進行的渲染都稱為離屏渲染,那麼就還有另一種特殊的“離屏渲染”方式:<code>CPU</code>渲染。如果我們重寫了  <code>drawRect</code>方法,并且使用任何 <code>Core Graphics</code>的技術進行了繪制操作,就涉及到了<code>CPU</code>渲染。整個渲染過程由 <code>CPU</code>在  <code>App</code>内同步地完成,渲染得到的  <code>bitmap</code>最後再交由 <code> GPU</code>用于顯示。

現在擺在我們面前得有三個選擇:目前螢幕渲染、離屏渲染、<code>CPU</code>渲染,該用哪個呢?這需要根據具體的

使用場景來決定。

盡量使用目前螢幕渲染

鑒于離屏渲染、CPU渲染可能帶來的性能問題,一般情況下,我們要盡量使用目前螢幕渲染。

離屏渲染  VS CPU渲染

由于 <code>GPU</code>的浮點運算能力比 <code>CPU</code>強,<code>CPU</code>渲染的效率可能不如離屏渲染;但如果僅僅是實作一個簡單的效果,直接使用<code>CPU</code>渲染的效率又可能比離屏渲染好,畢竟離屏渲染要涉及到緩沖區建立和上下文切換等耗時操作

<code>UIButton</code>的   <code>masksToBounds</code> = <code>YES</code>又設定 <code> setImage</code>、<code>setBackgroundImage</code>、<code>[buttonsetBackgroundColor:[UIColor colorWithPatternImage:[UIImage imageNamed:@"btn_selected"]]];</code>

下發生離屏渲染,但是<code>[button setBackgroundColor:[UIColor redColor]]</code>;是不會出現離屏渲染的

關于<code> UIImageView</code>,現在測試發現(現版本: iOS10),在性能的範圍之内,給 <code>UIImageView</code>設定圓角是不會觸發離屏渲染的,但是同時給 <code>UIImageView</code>設定背景色則肯定會觸發.觸發離屏渲染跟 <code> png.jpg</code>格式并無關聯

日常我們使用<code>layer</code>的兩個屬性,實作圓角

<code>imageView.layer.cornerRaidus = CGFloat(10);</code>

<code>imageView.layer.masksToBounds = YES;</code>

這樣處理的渲染機制是<code>GPU</code>在目前螢幕緩沖區外新開辟一個渲染緩沖區進行工作,也就是離屏渲染,這會給我們帶來額外的性能損耗。如果這樣的圓角操作達到一定數量,會觸發緩沖區的頻繁合并和上下文的頻繁切換,性能的代價會宏觀地表現在使用者體驗上——掉幀

1、模拟器 <code>debug-</code>選中  <code> color blended layers</code>紅色區域表示圖層發生了混合

2、<code>Instrument-</code>選中<code>Core Animation-</code>勾選<code>Color Blended Layers</code>

避免圖層混合:

1、確定控件的<code>opaque</code>屬性設定為<code>true</code>,確定 <code>backgroundColor</code>和父視圖顔色一緻且不透明

2、如無特殊需要,不要設定低于 1的<code>alpha</code>值

3、確定<code>UIImage</code>沒有 <code>alpha</code>通道

UILabel圖層混合解決方法:

iOS8以後設定背景色為非透明色并且設定 <code> label.layer.masksToBounds=YES</code>讓<code>label</code>隻會渲染她的實際 <code> size</code>區域,就能解決<code>UILabel</code>的圖層混合問題

iOS8之前隻要設定背景色為非透明的就行

為什麼設定了背景色但是在 iOS8上仍然出現了圖層混合呢?

<code>UILabel</code>在  iOS8前後的變化,在 iOS8以前,<code>UILabel</code>使用的是   CALayer作為底圖層,而在  iOS8開始,<code>UILabel</code>的底圖層變成了<code>_UILabelLayer</code>,繪制文本也有所改變。在背景色的四周多了一圈透明的邊,而這一圈透明的邊明顯超出了圖層的矩形區域,設定圖層的 <code>masksToBounds</code>為 <code>YES</code>時,圖層将會沿着 <code> Bounds</code>進行裁剪圖層混合問題解決了.