12.3 Instruments

2018-02-24 15:07 更新

Instruments

????Instruments是Xcode套件中沒有被充分利用的一個(gè)工具。很多iOS開發(fā)者從沒用過Instruments,或者只是用Leaks工具檢測(cè)循環(huán)引用。實(shí)際上有很多Instruments工具,包括為動(dòng)畫性能調(diào)優(yōu)的東西。

????你可以通過在菜單中選擇Profile選項(xiàng)來打開Instruments(在這之前,記住要把目標(biāo)設(shè)置成iOS設(shè)備,而不是模擬器)。然后將會(huì)顯示出圖12.1(如果沒有看到所有選項(xiàng),你可能設(shè)置成了模擬器選項(xiàng))。

圖12.2 添加額外的工具到Instruments側(cè)邊欄

時(shí)間分析器

????時(shí)間分析器工具用來檢測(cè)CPU的使用情況。它可以告訴我們程序中的哪個(gè)方法正在消耗大量的CPU時(shí)間。使用大量的CPU并不一定是個(gè)問題 - 你可能期望動(dòng)畫路徑對(duì)CPU非常依賴,因?yàn)閯?dòng)畫往往是iOS設(shè)備中最苛刻的任務(wù)。

????但是如果你有性能問題,查看CPU時(shí)間對(duì)于判斷性能是不是和CPU相關(guān),以及定位到函數(shù)都很有幫助(見圖12.3)。

圖12.4 使用可視化調(diào)試選項(xiàng)的Core Animation工具

????Core Animation工具也提供了一系列復(fù)選框選項(xiàng)來幫助調(diào)試渲染瓶頸:

  • Color Blended Layers?- 這個(gè)選項(xiàng)基于渲染程度對(duì)屏幕中的混合區(qū)域進(jìn)行綠到紅的高亮(也就是多個(gè)半透明圖層的疊加)。由于重繪的原因,混合對(duì)GPU性能會(huì)有影響,同時(shí)也是滑動(dòng)或者動(dòng)畫幀率下降的罪魁禍?zhǔn)字弧?/p>

  • ColorHitsGreenandMissesRed?- 當(dāng)使用shouldRasterizep屬性的時(shí)候,耗時(shí)的圖層繪制會(huì)被緩存,然后當(dāng)做一個(gè)簡(jiǎn)單的扁平圖片呈現(xiàn)。當(dāng)緩存再生的時(shí)候這個(gè)選項(xiàng)就用紅色對(duì)柵格化圖層進(jìn)行了高亮。如果緩存頻繁再生的話,就意味著柵格化可能會(huì)有負(fù)面的性能影響了(更多關(guān)于使用shouldRasterize的細(xì)節(jié)見第15章“圖層性能”)。

  • Color Copied Images?- 有時(shí)候寄宿圖片的生成意味著Core Animation被強(qiáng)制生成一些圖片,然后發(fā)送到渲染服務(wù)器,而不是簡(jiǎn)單的指向原始指針。這個(gè)選項(xiàng)把這些圖片渲染成藍(lán)色。復(fù)制圖片對(duì)內(nèi)存和CPU使用來說都是一項(xiàng)非常昂貴的操作,所以應(yīng)該盡可能的避免。

  • Color Immediately?- 通常Core Animation Instruments以每毫秒10次的頻率更新圖層調(diào)試顏色。對(duì)某些效果來說,這顯然太慢了。這個(gè)選項(xiàng)就可以用來設(shè)置每幀都更新(可能會(huì)影響到渲染性能,而且會(huì)導(dǎo)致幀率測(cè)量不準(zhǔn),所以不要一直都設(shè)置它)。

  • Color Misaligned Images?- 這里會(huì)高亮那些被縮放或者拉伸以及沒有正確對(duì)齊到像素邊界的圖片(也就是非整型坐標(biāo))。這些中的大多數(shù)通常都會(huì)導(dǎo)致圖片的不正??s放,如果把一張大圖當(dāng)縮略圖顯示,或者不正確地模糊圖像,那么這個(gè)選項(xiàng)將會(huì)幫你識(shí)別出問題所在。

  • Color Offscreen-Rendered Yellow?- 這里會(huì)把那些需要離屏渲染的圖層高亮成黃色。這些圖層很可能需要用shadowPath或者shouldRasterize來優(yōu)化。

  • Color OpenGL Fast Path Blue?- 這個(gè)選項(xiàng)會(huì)對(duì)任何直接使用OpenGL繪制的圖層進(jìn)行高亮。如果僅僅使用UIKit或者Core Animation的API,那么不會(huì)有任何效果。如果使用GLKView或者CAEAGLLayer,那如果不顯示藍(lán)色塊的話就意味著你正在強(qiáng)制CPU渲染額外的紋理,而不是繪制到屏幕。

  • Flash Updated Regions?- 這個(gè)選項(xiàng)會(huì)對(duì)重繪的內(nèi)容高亮成黃色(也就是任何在軟件層面使用Core Graphics繪制的圖層)。這種繪圖的速度很慢。如果頻繁發(fā)生這種情況的話,這意味著有一個(gè)隱藏的bug或者說通過增加緩存或者使用替代方案會(huì)有提升性能的空間。

????這些高亮圖層的選項(xiàng)同樣在iOS模擬器的調(diào)試菜單也可用(圖12.5)。我們之前說過用模擬器測(cè)試性能并不好,但如果你能通過這些高亮選項(xiàng)識(shí)別出性能問題出在什么地方的話,那么使用iOS模擬器來驗(yàn)證問題是否解決也是比真機(jī)測(cè)試更有效的。

圖12.6 OpenGL ES驅(qū)動(dòng)工具

側(cè)欄的郵編是一系列有用的工具。其中和Core Animation性能最相關(guān)的是如下幾點(diǎn):

  • Renderer Utilization?- 如果這個(gè)值超過了~50%,就意味著你的動(dòng)畫可能對(duì)幀率有所限制,很可能因?yàn)殡x屏渲染或者是重繪導(dǎo)致的過度混合。

  • Tiler Utilization?- 如果這個(gè)值超過了~50%,就意味著你的動(dòng)畫可能限制于幾何結(jié)構(gòu)方面,也就是在屏幕上有太多的圖層占用了。

一個(gè)可用的案例

????現(xiàn)在我們已經(jīng)對(duì)Instruments中動(dòng)畫性能工具非常熟悉了,那么可以用它在現(xiàn)實(shí)中解決一些實(shí)際問題。

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

清單12.1 使用假數(shù)據(jù)的一個(gè)簡(jiǎn)單聯(lián)系人列表

#import "ViewController.h"
#import 

@interface ViewController () 

@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)快速滑動(dòng)的時(shí)候就會(huì)非??ǎㄒ妶D12.7的FPS計(jì)數(shù)器)。

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

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

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

????屏幕中所有紅色的部分都意味著字符標(biāo)簽視圖的高級(jí)別混合,這很正常,因?yàn)槲覀儼驯尘霸O(shè)置成了透明色來顯示陰影效果。這就解釋了為什么渲染利用率這么高了。

????那么離屏繪制呢?打開Core Animation工具的Color Offscreen - Rendered Yellow選項(xiàng)(圖12.11)。

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

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

????好吧,每一行的字符和頭像在每一幀刷新的時(shí)候并不需要變,所以看起來UITableViewCell的圖層非常適合做緩存。我們可以使用shouldRasterize來緩存圖層內(nèi)容。這將會(huì)讓圖層離屏之后渲染一次然后把結(jié)果保存起來,直到下次利用的時(shí)候去更新(見清單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;
}

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

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)