app 效能最佳化的那些事(二)

來源:互聯網
上載者:User

標籤:

 

來源:樹下的老男孩 

連結:http://www.jianshu.com/p/2a01e5e2141f

 

這次我們來說說iOS app中滑動的那些事。iOS為了提高滑動的流暢感,特意在滑動的時候將runloop模式切換到UITrackingRunLoopMode,在這個過程中專心做跟滑動相關的工作,這也就是在滑動過程中為什麼nstimer無法工作的原因,因為兩個沒在同一mode下面。但我們可能經常會遇到滑動不怎麼流暢的情況,比如在項目中碰到在滑動tableview的時候不怎麼順暢,感覺有點不爽,即便是在測試中表現最好的5s(touch之類的感受更直觀)。

 

    tableview 滑動不流暢

那碰到這種情況該怎麼處理,分析映像動畫效能主要用的是Core Animation這個組件,先簡單介紹一下裡面一些經常用到的選項:

 

  • Color Blended layers

    標示混合的圖層會為紅色,不透明的圖層為綠色,通常我們希望綠色的地區越多越好。

  • Color Hits Green and Misses Red

    假如我們設定viewlayer的shouldRasterize為YES,那些成功被緩衝的layer會標註為綠色,反之為紅色,下面會有詳細介紹。

  • Color copied images

    標示那些被Core Animation拷貝的圖片。這主要是因為該圖片的色彩格式不能被GPU直接處理,需要在CPU這邊做轉換,假如在主線層做這個操作對效能會有一定的影響。

  • Color misaligned images

    被縮放的圖片會被標記為黃色,像素不對齊則會標註為紫色。

  • Color offscreen-rendered yellow

    標示哪些layer需要做離屏渲染(offscreen-render)。

 

 

簡單介紹完Core Animation的一些東西之後我們回過頭來看看哪些問題會影響到圖形的效能,下面這張圖摘自WWDC2014(Advanced Graphics and Animations for iOS Apps,這上面的一些分享非常有技術性)

 

      performance investigation mindset.png

當你碰到效能問題的時候,你可以思考一下:

 

是否受到CPU或者GPU的限制?

是否有不必要的CPU渲染?

是否有太多的離屏渲染操作?

是否有太多的圖層混合操作?

是否有奇怪的圖片格式或者尺寸?

是否涉及到昂貴的view或者效果?

view的階層是否合理?

 

那麼哪些是你最該開始考慮的方向呢?通常發生圖形效能問題的時候,比如列表滑動不順暢、動畫卡頓等,大部分都是由於Offscreen Rendering(離屏渲染)或者blending導致的,因為這在動畫的每一幀都會涉及到。

 

offscreen-render

 

什麼是offscreen-render?offscreen-render涉及的內容比較多,有offscreen-render那就有onscreen render,onscreen render指的是GPU在當前用於顯示的螢幕緩衝區進行渲染,相反offscreen-render就是不在當前的螢幕緩衝區,而在另外的緩衝區進行渲染,offscreen-render有兩種形式:

 

CPU的offscreen-render

 

使用CPU來完成渲染操縱,通常在你使用:

 

  • drawRect (如果沒有自訂繪製的任務就不要在子類中寫一個空的drawRect方法,因為只要實現了該方法,就會為視圖分配一個寄宿圖,這個寄宿圖的像素尺寸等於視圖大小乘以 contentsScale的值,造成資源浪費)

  • 使用Core Graphics

    上面的兩種情況使用的就是CPU離屏渲染,首先分配一塊記憶體,然後進行渲染操作產生一份bitmap位元影像,整個渲染過程會在你的應用中同步的進行,接著再將位元影像打包發送到iOS裡一個單獨的進程–render server,理想情況下,render server將內容交給GPU直接顯示到螢幕上。

 

GPU的offscreen-render

 

使用GPU在當前螢幕緩衝區以外開闢一個新的緩衝區進行繪製,通常發生的情況有:

 

  • 設定cornerRadius, masks, shadows,edge antialiasing等

  • 設定layer.shouldRasterize = YES

     

                    渲染流程

offscreen-render對效能到底有什麼影響?通常大家說的離屏渲染指的是GPU這塊(當然CPU這塊也會有影響,也需要消耗一定的資源),比如修改了layer的陰影或者圓角,GPU需要做額外的渲染操作。通常GPU在做渲染的時候是很快的,但是涉及到offscreen-render的時候情況就可能有些不同,因為需要額外開闢一個新的緩衝區進行渲染,然後繪製到當前螢幕的過程需要做onscreen跟offscreen上下文之間的切換,這個過程的消耗會比較昂貴,涉及到OpenGL的pipeline跟barrier,而且offscreen-render在每一幀都會涉及到,因此處理不當肯定會對效能產生一定的影響,所以可以的話盡量減少offscreen-render的圖層,查看哪些圖層需要離屏渲染可以用Instruments的Core Animation工具進行檢測,Color Offscreen-Rendered Yellow選項會將對應的圖層標記為黃色。

 

Blending

 

假如最上層的view是不透明的,那直接使用這個view的對應顏色之就可以,但如果view是透明的,在計算像素的顏色值時就需要計算它下面圖層,透明的視圖越多,計算量就越大,因此也會對圖形的效能產生一定的影響,所以可以的話也盡量減少透明圖層的數目。

 

Demo

 

下面給出一個簡單demo(https://github.com/FreeMind-LJ/OptimiseDemo)的最佳化過程,這個demo裡面涉及到的問題是在實際項目中所碰到的,也就是最上面那張圖裡列表滑動不流暢情況—由陰影以及圓角導致的offscreen-render。

整個頁面就是一個簡單的tableview,其中頭像為圓角,一個label有陰影製作效果,滑動的時候在iPod上幀率只有可憐的28FPS。

 

Color Offscreen-Rendered Yellow

28FPS.png

其中黃色的地區就是離屏渲染的地方,也就是含有圓角跟陰影的layer。

 

shadowPath

 

設定label的陰影製作效果可以通過:

 

cell.sign.layer.shadowOffset = CGSizeMake(0, 2);

    cell.sign.layer.shadowOpacity = 0.5;

    cell.sign.layer.shadowColor = [UIColor blackColor].CGColor;

 

但是你可以發現這會導致離屏渲染,一個簡單的不需要離屏渲染的方法就是制定陰影的路徑,也就是設定layer的shadowPath屬性,通過instruments發現陰影的地方沒有黃色了,幀率也提高到了40FPS:

 

cell.sign.layer.shadowPath = [UIBezierPath  bezierPathWithRect:cell.sign.bounds].CGPath;

 

設定shadowPath消除離屏渲染.png

 

rasterize

 

對於圓角這種類似導致的效能問題,最簡單的就是在列表中不要使用圓角,假如要使用圓角的話,一種最快提升效能的方式就是設定layer的shouldRasterize為YES:

 

cell.layer.shouldRasterize = YES;

    cell.layer.rasterizationScale = [UIScreen mainScreen].scale;

 

雖然被Rasterize的圖層也會引起離屏渲染,如所示,整個cell都被標示為黃色:

 

shouldRasterize.png

layer設定shouldRasterize=YES之後,會把被光柵化的圖層儲存成位元影像並緩衝起來,其中圓角或者陰影之類的效果也是直接儲存到位元影像當中,當需要渲染到螢幕上的時候只需要到緩衝中去取對應的位元影像進行顯示就行了,加快了整個渲染過程。可以通過勾選instruments core animation中的Color Hits Green and Misses Red選項來查看圖層是否被緩衝了,如果圖層顯示為綠色則表示已經被緩衝起來了,也就是這個緩衝區的內容被複用了,不用在去重新建立緩衝區,反之則是用紅色標示。如可以看到設定shouldRasterize之後,cell都被標示為綠色了,如果滑動過程中發現都是紅色的證明就有問題了:

 

cached.png

再看看現在滑動的幀率:

最佳化後.png

可以發現現在滾動的效能大大提高了,光柵化對於那些有很多子view嵌套在一起、view的層級複雜或者有很複雜特效效果的圖層有很明顯的提升,因為這些內容都被緩衝到位元影像當中了。但是使用光柵化需要注意一些內容:

 

  • 適用於內容基本不變的圖層

    假層的內容經常變化,比如cell裡面有涉及到動畫之類的,那麼緩衝的內容就無效了,GPU需要重新建立緩衝區,導致離屏渲染,這又涉及到OpenGL的上下文環境切換,反而降低效能。

  • 不要過度使用

    緩衝區的大小被設定為螢幕大小的2.5倍,假如過分使用同樣會導致大量的離屏渲染。

  • 如果緩衝的內容超過100ms沒有被使用則會被回收。

 

 

tips

 

  • 對於圓角可以使用一張中間圓形透明的圖覆蓋在上面,雖然這會引入blending操作,但是大部分情況下效能會比離屏渲染好。

  • 讓你的view階層平坦一些,因為OpenGL在渲染layer的時候,在碰到有子層級layer的時候可能需要停下來把兩者合成到一個buffer裡再接著渲染。(When the OpenGL renderer goes to draw each layer, it may have to stop for some subhierarchies and composite them into a single buffer).

  • 消極式載入圖片

    有時候在邊滾動邊設定圖片的時候可能會有一定的影響,因此可以在滾動的時候imageview不執行setimage的操作,滾動停止的時候才載入圖片,由於滾動的時候NSRunloop是處於UITrackingRunLoopMode模式下,可以採用如下的方式,將設定圖片放到NSDefaultRunLoopMode模式下才進行:

 

UIImage *downloadedImage = ...;

  [self.avatarImageView performSelector:@selector(setImage:)

                             withObject:downloadedImage

                             afterDelay:0

                                inModes:@[NSDefaultRunLoopMode]];

 

  • 圖片載入的極限最佳化方式:FastImageCache

    https://github.com/path/FastImageCache

 

 

圖形效能這塊有什麼好的想法也可提出來交流一下~~

 

參考:

 

  • https://lobste.rs/s/ckm4uw/a_performance-minded_take_on_ios_design/comments/itdkfh

  • Advanced Graphics and Animations for iOS Apps

  • http://iosinjordan.tumblr.com/post/56778173518/help-my-tables-dont-scroll-smoothly

     

 

app 效能最佳化的那些事(二)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.