每日一问24——UiTableview性能调优

前言

UITableView是我们开发APP最常使用到的控件之一,使用它可以向用户展示大量的数据和信息,为了把这些数据更好的展示给用户,tableview的样式可能会变得非常复杂但美观。在我们制作出一个好看的tableview的同时,性能问题也逐渐走入了我们的视线。

为了让tableview的在美观的同时保持良好的性能,我们需要了解影响tableview性能的原因还有如何优化这些问题。
首先我们要知道,我们滑动时看到界面卡顿是由CPU和GPU开销共同决定的(参考每日一问02——渲染流程

1.cell重用

一个tableview中往往会存在很多cell,如果每显示一个cell都要重新加载,那需要的开销将会是非常庞大的。苹果已经为我们提供了最基本的cell重用机制。

//将cell加载到内存
- (void)registerNib:(nullable UINib *)nib forCellReuseIdentifier:(NSString *)identifier NS_AVAILABLE_IOS(5_0);
- (void)registerClass:(nullable Class)cellClass forCellReuseIdentifier:(NSString *)identifier NS_AVAILABLE_IOS(6_0);
//使用内存中可重用的cell
- (nullable __kindof UITableViewCell *)dequeueReusableCellWithIdentifier:(NSString *)identifier;

2.高度计算

当只有一种高度的情况下,我们可以直接指定cell的高度,这样是没有性能影响的。但很多时候会因为内容换行,图片等不确定因素导致行高不一致。这个时候就需要计算行高或者让系统计算行高。

>动态计算高度

iOS7后,系统给我们提供了estimatedRowHeight这个属性,让我们预估一个cell的高度由系统根据具体的约束计算cell高度。

self.tableView.estimatedRowHeight = 80;
>手动计算高度

很多时候我们会通过cell内控件的布局,约束,文字高度来计算这个cell的具体高度。我们会在代理方法heightForRowAtIndexPath中返回cell的具体高度。

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
    return [cell getHeight];
}

这样做虽然解决了动态高度cell的展示,但会因为每加载一个cell就会进行一次计算导致CPU计算量增加,如果cell中布局非常复杂,那这个消耗是很大的。

>缓存计算高度

对于计算过一次的高度来讲,我们不要再次计算,所以我们可以把计算好的高度进行缓存。保存再model里。当需要展示同一个cell时就可以直接使用上一次计算出来的高度。

3.渲染

渲染是由CPU和GPU共同完成的,在一个 VSync 时间内,CPU 或者 GPU 没有完成内容提交,则那一帧就会被丢弃。就造成了我们看到的卡顿现象。而CPU消耗主要在各种计算上,图形图像的计算主要则是靠GPU完成的。所以我们需要让CPU和GPU利用率尽可能的均衡。

>减少GPU开销
  • 尽量使用不透明的视图进行布局
    当多个透明图像重叠时,GPU会进行计算,合成出一个颜色,这样的开销无疑是巨大的。所以在没有特殊需求的时候,我们可以设置layer的opaque来关闭合成,使用简单的拷贝图层而不考虑这个图层下面的东西。
  • 减少离屏渲染
    离屏渲染主要在两个地方开销较大:
1.创建新缓冲区,要想进行离屏渲染,首先要创建一个新的缓冲区。
2.上下文切换

我们要知道,CPU和GPU都可能造成离屏渲染。但我们可以让CPU和GPU负载均衡来保证渲染的性能。例如Core Graphics的所有API都会造成CPU的离屏渲染。

造成离屏渲染的主要方式
  • cornerRadius(圆角)
  • masks(遮罩)
  • shadows(阴影)
  • edge antialiasing(抗锯齿)
  • group opacity(不透明)

我们遇到的最多的就是添加圆角造成的离屏渲染,解决方案就是使用CPU来绘制圆角图片:参考(每日一问06——imageView的圆角优化

>减少CPU开销
  • 图片预渲染
    当我们使用imageWithContentsOfFile加载一张图片时,图片只是被加载到了内存中并没有被渲染到UIimageView上,当UIImageView进行渲染时才会在主线程将这个图片解压成位图,这个解压的过程是一个非常耗时的CPU操作。

解决方案就是:在子线程预先对图片进行强制解压,生成可用的位图(每日一问04-加载图片对性能的影响

4.异步

我们都知道UI操作是在主线程执行的,所以我们可以尽可能避免在主线程进行资源加载或对数据进行计算。把这些操作放在子线程中进行再显示在UI上。最常见的例子就是异步加载网络图片并显示。

5.减少多余的计算

很多时候tableview中会加载很多cell,而屏幕中只能展示其中那么几个。所以在滑动的过程中,如果我们只是在cellForRowAtIndexPath中对每一个cell都开线程进行图片或其他资源的加载,那么消耗将会非常大。

解决方案就是在快速滑动过程中,不执行异步加载,当滚动开始减速的时候才加载显示在当前屏幕上的cell。

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{

 UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];
    //如果没有取到,就初始化
    if (!cell) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"Cell"];
    }
    cell.textLabel.text = @"Name";

    BOOL canLoad = !self.tableView.dragging && !_tableView.decelerating;
    if  (canLoad) {
        //开始loaddata,异步加载图片
        NSLog(@"开始加载图片");
    }
    return cell;
}


// 滚动停止时,触发该函数
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
    //刷新tableview
   // [self.tableView reloadData];


 //在此刷新的是屏幕上显示的cell的内容
    NSArray *visiblePaths = [self.tableView indexPathsForVisibleRows];
    //获取到的indexpath为屏幕上的cell的indexpath
    [self.tableView reloadRowsAtIndexPaths:visiblePaths withRowAnimation:UITableViewRowAnimationRight];

}

6.使用统一的cell

有的时候,我们界面上会存在许多样式不同,但布局方式很相似的cell。这时我们可以只编写一个cell样式,通过不同需求改变cell样式。这样做的好处有两点

1.减少代码量,减少Nib文件的数量,统一一个Nib文件定义Cell,容易修改、维护。
2.基于Cell的重用,真正运行时铺满屏幕所需的Cell数量大致是固定的,设为N个。所以如果如果只有一种Cell,那就是只有N个Cell的实例;但是如果有M种Cell,那么运行时最多可能会是“M x N = MN”个Cell的实例

在改变cell样式时,我们应尽量避免动态添加,移除subView,这样的操作会带来很多额外的计算。我们应该使用hidden方式直接隐藏和显示subView,这样要比重新创建subView性能高得多。

相关文章

提升UITableView性能-复杂页面的优化
优化UITableViewCell高度计算的那些事
iOS UITableView性能优化
iOS Tableview优化

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,080评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,422评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,630评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,554评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,662评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,856评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,014评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,752评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,212评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,541评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,687评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,347评论 4 331
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,973评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,777评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,006评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,406评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,576评论 2 349

推荐阅读更多精彩内容