調(diào)試工具Instruments----案例

學(xué)習(xí)案例

創(chuàng)建一個簡單的顯示模擬聯(lián)系人姓名和頭像列表的應(yīng)用。注意即使把頭像圖片存在應(yīng)用本地,為了使應(yīng)用看起來更真實,分別實時加載圖片,而不是用–imageNamed:預(yù)加載。同樣添加一些圖層陰影來使得列表顯示得更真實。


#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

當(dāng)快速滑動的時候就會非常卡

滑動幀率降到15FPS

僅憑直覺,我們猜測性能瓶頸應(yīng)該在圖片加載。我們實時從閃存加載圖片,而且沒有緩存,所以很可能是這個原因。我們可以用一些很贊的代碼修復(fù),然后使用GCD異步加載圖片,然后緩存。。。等一下,在開始編碼之前,測試一下假設(shè)是否成立。首先用我們的三個Instruments工具分析一下程序來定位問題。我們推測問題可能和圖片加載相關(guān),所以用Time Profiler工具來試試。

用The timing profile分析聯(lián)系人列表

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

OpenGL ES Driver工具顯示的GPU利用率

渲染服務(wù)利用率的值達(dá)到51%和63%??雌饋鞧PU需要做很多工作來渲染聯(lián)系人列表。

為什么GPU利用率這么高呢?我們來用Core Animation調(diào)試工具選項來檢查屏幕。首先打開Color Blended Layers。

使用Color Blended Layers選項調(diào)試程序

屏幕中所有紅色的部分都意味著字符標(biāo)簽視圖的高級別混合,這很正常,因為我們把背景設(shè)置成了透明色來顯示陰影效果。這就解釋了為什么渲染利用率這么高了。

那么離屏繪制呢?打開Core Animation工具的Color Offscreen - Rendered Yellow選項。

Color Offscreen–Rendered Yellow選項

所有的表格單元內(nèi)容都在離屏繪制。這一定是因為我們給圖片和標(biāo)簽視圖添加的陰影效果。在代碼中禁用陰影,然后看下性能是否有提高。

禁用陰影之后運(yùn)行程序接近60FPS

問題解決了。干掉陰影之后,滑動很流暢。但是我們的聯(lián)系人列表看起來沒有之前好了。那如何保持陰影效果而且不會影響性能呢?

好吧,每一行的字符和頭像在每一幀刷新的時候并不需要變,所以看起來UITableViewCell的圖層非常時候做緩存。我們可以使用shouldRasterize來緩存圖層內(nèi)容。這將會讓圖層離屏之后渲染一次然后把結(jié)果保存起來,直到下次利用的時候去更新。

使用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;

}

我們?nèi)匀浑x屏繪制圖層內(nèi)容,但是由于顯式地禁用了柵格化,Core Animation就對繪圖緩存了結(jié)果,于是對提高了性能。我們可以驗證緩存是否有效,在Core Animation工具中點擊Color Hits Green and Misses Red選項。

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

結(jié)果和預(yù)期一致 - 大部分都是綠色,只有當(dāng)滑動到屏幕上的時候會閃爍成紅色。因此,現(xiàn)在幀率更加平滑了。

所以我們最初的設(shè)想是錯的。圖片的加載并不是真正的瓶頸所在,而且試圖把它置于一個復(fù)雜的多線程加載和緩存的實現(xiàn)都將是徒勞。所以在動手修復(fù)之前驗證問題所在是個很好的習(xí)慣!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容