[iOS Animation]-CALayer 性能優化實例

一個可用的案例

如今咱們已經對Instruments中動畫性能工具很是熟悉了,那麼能夠用它在現實中解決一些實際問題。git

咱們建立一個簡單的顯示模擬聯繫人姓名和頭像列表的應用。注意即便把頭像圖片存在應用本地,爲了使應用看起來更真實,咱們分別實時加載圖片,而不是用–imageNamed:預加載。一樣添加一些圖層陰影來使得列表顯示得更真實。清單12.1展現了最第一版本的實現。github

清單12.1 使用假數據的一個簡單聯繫人列表緩存

複製代碼

#import "ViewController.h"#import <QuartzCore/QuartzCore.h>@interface ViewController () <UITableViewDataSource>@property (nonatomic, strong) NSArray *items;
@property (nonatomic, weak) IBOutlet UITableView *tableView;@end@implementation ViewController- (NSString *)randomName
{
    NSArray *first = @[@"Alice", @"Bob", @"Bill", @"Charles", @"Dan", @"Dave", @"Ethan", @"Frank"];
    NSArray *last = @[@"Appleseed", @"Bandicoot", @"Caravan", @"Dabble", @"Ernest", @"Fortune"];
    NSUInteger index1 = (rand()/(double)INT_MAX) * [first count];
    NSUInteger index2 = (rand()/(double)INT_MAX) * [last count];    return [NSString stringWithFormat:@"%@ %@", first[index1], last[index2]];
}- (NSString *)randomAvatar
{
    NSArray *images = @[@"Snowman", @"Igloo", @"Cone", @"Spaceship", @"Anchor", @"Key"];
    NSUInteger index = (rand()/(double)INT_MAX) * [images count];    return images[index];
}- (void)viewDidLoad
{
    [super viewDidLoad];    //set up data
    NSMutableArray *array = [NSMutableArray array];    for (int i = 0; i < 1000; i++) {
        //add name
        [array addObject:@{@"name": [self randomName], @"image": [self randomAvatar]}];
    }
    self.items = array;    //register cell class
    [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"Cell"];
}- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{    return [self.items count];
}- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{    //dequeue cell
    UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];    //load image
    NSDictionary *item = self.items[indexPath.row];
    NSString *filePath = [[NSBundle mainBundle] pathForResource:item[@"image"] ofType:@"png"];    //set image and text
    cell.imageView.image = [UIImage imageWithContentsOfFile:filePath];
    cell.textLabel.text = item[@"name"];    //set image shadow
    cell.imageView.layer.shadowOffset = CGSizeMake(0, 5);
    cell.imageView.layer.shadowOpacity = 0.75;
    cell.clipsToBounds = YES;    //set text shadow
    cell.textLabel.backgroundColor = [UIColor clearColor];
    cell.textLabel.layer.shadowOffset = CGSizeMake(0, 2);
    cell.textLabel.layer.shadowOpacity = 0.5;    return cell;
}@end

複製代碼

 

當快速滑動的時候就會很是卡(見圖12.7的FPS計數器)。多線程

圖12.7

圖12.7 滑動幀率降到15FPSdom

僅憑直覺,咱們猜想性能瓶頸應該在圖片加載。咱們實時從閃存加載圖片,並且沒有緩存,因此極可能是這個緣由。咱們能夠用一些很讚的代碼修復,而後使用GCD異步加載圖片,而後緩存。。。等一下,在開始編碼以前,測試一下假設是否成立。首先用咱們的三個Instruments工具分析一下程序來定位問題。咱們推測問題可能和圖片加載相關,因此用Time Profiler工具來試試(圖12.8)。異步

圖12.8

圖12.8 用The timing profile分析聯繫人列表工具

 -tableView:cellForRowAtIndexPath: 中的CPU時間總利用率只有~28%(也就是加載頭像圖片的地方),很是低。因而建議是CPU/IO並非真正的限制因素。而後看看是否是GPU的問題:在OpenGL ES Driver工具中檢測GPU利用率(圖12.9)。性能

圖12.9

圖12.9 OpenGL ES Driver工具顯示的GPU利用率學習

渲染服務利用率的值達到51%和63%。看起來GPU須要作不少工做來渲染聯繫人列表。測試

爲何GPU利用率這麼高呢?咱們來用Core Animation調試工具選項來檢查屏幕。首先打開Color Blended Layers(圖12.10)。

圖12.10

圖12.10 使用Color Blended Layers選項調試程序

屏幕中全部紅色的部分都意味着字符標籤視圖的高級別混合,這很正常,由於咱們把背景設置成了透明色來顯示陰影效果。這就解釋了爲何渲染利用率這麼高了。

那麼離屏繪製呢?打開Core Animation工具的Color Offscreen - Rendered Yellow選項(圖12.11)。

圖12.11

圖12.11 Color Offscreen–Rendered Yellow選項

全部的表格單元內容都在離屏繪製。這必定是由於咱們給圖片和標籤視圖添加的陰影效果。在代碼中禁用陰影,而後看下性能是否有提升(圖12.12)。

圖12.12

圖12.12 禁用陰影以後運行程序接近60FPS

問題解決了。幹掉陰影以後,滑動很流暢。可是咱們的聯繫人列表看起來沒有以前好了。那如何保持陰影效果並且不會影響性能呢?

好吧,每一行的字符和頭像在每一幀刷新的時候並不須要變,因此看起來UITableViewCell的圖層很是適合作緩存。咱們可使用shouldRasterize來緩存圖層內容。這將會讓圖層離屏以後渲染一次而後把結果保存起來,直到下次利用的時候去更新(見清單12.2)。

清單12.2 使用shouldRasterize提升性能

複製代碼

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{    //dequeue cell
    UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:@"Cell"
                                                                 forIndexPath:indexPath];
    ...    //set text shadow
    cell.textLabel.backgroundColor = [UIColor clearColor];
    cell.textLabel.layer.shadowOffset = CGSizeMake(0, 2);
    cell.textLabel.layer.shadowOpacity = 0.5;    //rasterize
    cell.layer.shouldRasterize = YES;
    cell.layer.rasterizationScale = [UIScreen mainScreen].scale;    return cell;
}

複製代碼

 

咱們仍然離屏繪製圖層內容,可是因爲顯式地禁用了柵格化,Core Animation就對繪圖緩存告終果,因而對提升了性能。咱們能夠驗證緩存是否有效,在Core Animation工具中點擊Color Hits Green and Misses Red選項(圖12.13)。

圖12.13

圖12.13 Color Hits Green and Misses Red驗證了緩存有效

結果和預期一致 - 大部分都是綠色,只有當滑動到屏幕上的時候會閃爍成紅色。所以,如今幀率更加平滑了。

因此咱們最初的設想是錯的。圖片的加載並非真正的瓶頸所在,並且試圖把它置於一個複雜的多線程加載和緩存的實現都將是徒勞。因此在動手修復以前驗證問題所在是個很好的習慣!

總結

在這章中,咱們學習了Core Animation是如何渲染,以及咱們可能出現的瓶頸所在。你一樣學習瞭如何使用Instruments來檢測和修復性能問題。

相關文章
相關標籤/搜索