美文网首页iOS-项目实战
每日一问24——UiTableview性能调优

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

作者: 巫师学徒 | 来源:发表于2017-10-26 15:38 被阅读48次

    前言

    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优化

    相关文章

      网友评论

        本文标题:每日一问24——UiTableview性能调优

        本文链接:https://www.haomeiwen.com/subject/piyqpxtx.html