博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[iOS Animation]-CALayer 性能优化实例
阅读量:6248 次
发布时间:2019-06-22

本文共 4643 字,大约阅读时间需要 15 分钟。

hot3.png

一个可用的案例

现在我们已经对Instruments中动画性能工具非常熟悉了,那么可以用它在现实中解决一些实际问题。

我们创建一个简单的显示模拟联系人姓名和头像列表的应用。注意即使把头像图片存在应用本地,为了使应用看起来更真实,我们分别实时加载图片,而不是用–imageNamed:预加载。同样添加一些图层阴影来使得列表显示得更真实。清单12.1展示了最初版本的实现。

清单12.1 使用假数据的一个简单联系人列表

复制代码

#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

复制代码

 

当快速滑动的时候就会非常卡(见图12.7的FPS计数器)。

图12.7 滑动帧率降到15FPS

仅凭直觉,我们猜测性能瓶颈应该在图片加载。我们实时从闪存加载图片,而且没有缓存,所以很可能是这个原因。我们可以用一些很赞的代码修复,然后使用GCD异步加载图片,然后缓存。。。等一下,在开始编码之前,测试一下假设是否成立。首先用我们的三个Instruments工具分析一下程序来定位问题。我们推测问题可能和图片加载相关,所以用Time Profiler工具来试试(图12.8)。

图12.8 用The timing profile分析联系人列表

 -tableView:cellForRowAtIndexPath: 中的CPU时间总利用率只有~28%(也就是加载头像图片的地方),非常低。于是建议是CPU/IO并不是真正的限制因素。然后看看是不是GPU的问题:在OpenGL ES Driver工具中检测GPU利用率(图12.9)。

图12.9 OpenGL ES Driver工具显示的GPU利用率

渲染服务利用率的值达到51%和63%。看起来GPU需要做很多工作来渲染联系人列表。

为什么GPU利用率这么高呢?我们来用Core Animation调试工具选项来检查屏幕。首先打开Color Blended Layers(图12.10)。

图12.10 使用Color Blended Layers选项调试程序

屏幕中所有红色的部分都意味着字符标签视图的高级别混合,这很正常,因为我们把背景设置成了透明色来显示阴影效果。这就解释了为什么渲染利用率这么高了。

那么离屏绘制呢?打开Core Animation工具的Color Offscreen - Rendered Yellow选项(图12.11)。

图12.11 Color Offscreen–Rendered Yellow选项

所有的表格单元内容都在离屏绘制。这一定是因为我们给图片和标签视图添加的阴影效果。在代码中禁用阴影,然后看下性能是否有提高(图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 Color Hits Green and Misses Red验证了缓存有效

结果和预期一致 - 大部分都是绿色,只有当滑动到屏幕上的时候会闪烁成红色。因此,现在帧率更加平滑了。

所以我们最初的设想是错的。图片的加载并不是真正的瓶颈所在,而且试图把它置于一个复杂的多线程加载和缓存的实现都将是徒劳。所以在动手修复之前验证问题所在是个很好的习惯!

总结

在这章中,我们学习了Core Animation是如何渲染,以及我们可能出现的瓶颈所在。你同样学习了如何使用Instruments来检测和修复性能问题。

转载于:https://my.oschina.net/u/2438875/blog/508123

你可能感兴趣的文章
Java规则引擎及JSR-94[转]
查看>>
【c学习-13】
查看>>
给报表增加页眉
查看>>
Mysql配置参数说明
查看>>
python ----字符串基础练习题30道
查看>>
K 班1-7,alpha,beta 作业成绩汇总
查看>>
uva-10879-因数分解
查看>>
清空表且自增的id重新从0开始
查看>>
[杂记]如何在LaTeX里插入高亮代码
查看>>
「常微分方程」(阿諾爾德) Page 6 問題4 經過擴張相空間的每一點有且僅有一條積分曲線...
查看>>
同一个闭区间上有界变差函数的和与积都是有界变差函数
查看>>
java安全证书配置
查看>>
使用erlang 建立一个自动化的灌溉系统(1)准备工作
查看>>
python 调用aiohttp
查看>>
mysql 案例~ mysql故障恢复
查看>>
Spring Boot中使用MyBatis注解配置详解
查看>>
MatLab实现FFT与功率谱
查看>>
答《漫话ID》中的疑问:UniqueID和ClientID的来源
查看>>
【转】Asp.net控件开发学习笔记整理篇 - 服务器控件生命周期
查看>>
Linux下的shell编程(一)BY 四喜三顺
查看>>