UITableView性能优化的一些经验

释放双眼,带上耳机,听听看~!

一、缓存高度
这个很重要,tableView获取高度的方法是调用很频繁的,如果每一个cell的高度都需要通过计算得到,那么将消耗挺多CPU处理时间。如果tableViewCell的高度是不一样的,那么最好在生成每个cell相关的model时,同时计算并保存对应cell的高度于model中,后面直接拿来用。你会发现tableView的性能有很大的提高。
参考:http://www.jishudog.com/1284/html

二、异步绘制
默认UIKit的UI组件都是在主线程中绘制的,如果一些组件的内容比较复杂,那么也会占用很多CPU时间。可以使用CoreGraphics的API进行绘制,因为CoreGraphics的API是线程安全的,所以可以放到子线程中绘制,代码类似如下:

dispatch_async(backgroundQueue, ^{
       CGContextRef ctx = CGBitmapContextCreate(...);
       // draw in context...
       CGImageRef img = CGBitmapContextCreateImage(ctx);
       CFRelease(ctx);
       dispatch_async(mainQueue, ^{
           layer.contents = img;
       });
   });

当然,如果你使用异步CoreGraphics的API来绘制组件,那么就是说你放弃了UIKit的那一套,所有的组件都得重新封装,工作量还是挺大的。
YYKit库已经帮我们封装了很多控件,都支持异步绘制。
参考:http://www.jishudog.com/1290/html

三、异步加载
对于像网络图片的加载,使用异步加载。相信大家都会使用SDWebImage库。大家都懂。使用这个库还有个好处,SDWebImage库在即将缓存网络图片之前,会进行图片的解码。那么后面展示的时候,就不需要解码了,对性能也有很大的提升。
解码对性能的影响参考:http://www.jishudog.com/1294/html

四、善用hidden
减少cell动态创建View,提前把所有可能的view布局好,然后通过控制view的隐藏和显示操作不同样式。

五、减少离屏渲染
离屏渲染:多图层,并且最上层的图层没有完全挡住下面的图层,那么都有可能发生离屏渲染。比如设置圆角、阴影、遮罩等。离屏渲染之所以会特别消耗性能,是因为要创建一个屏幕外的缓冲区,然后从当屏缓冲区切换到屏幕外的缓冲区,然后再完成渲染;其中,创建缓冲区和切换上下文最消耗性能,而绘制其实不是性能损耗的主要原因。大部分离屏渲染发生在GPU上,即将显示时候,会对需要合成的图层进行离屏渲染。
解决:可以通过CoreGraphic进行绘制,并且CoreGraphic绘制API是线程安全的,就可以在子线程绘制,进行图层合成,绘制完成在主线程展示。把渲染操作放到CPU中,减轻GPU的负担。
当然IOS9.0后,UIImageVIew设置圆角不会发生离屏渲染,所以可以使用:
headImageView1.layer.cornerRadius = 40;
headImageView1.clipsToBounds = YES;
对于圆角的离屏渲染测试,我暂时没发现很大的性能消耗,或许Xcode后面进行了优化。
参考:http://www.jishudog.com/1297/html

六、按需加载
对于快速滑动的tableView,可以考虑只绘制快速滑动即将停止附件的那些cell。对于活动中昙花一现的cell,没必要绘制。不过这可能导致出现空白的cell。如下操作:

//按需加载 - 如果目标行与当前行相差超过指定行数,只在目标滚动范围的前后指定3行加载。 当 velocity 不为 CGPointZero 时,scroll view 会以 velocity 为初速度,减速直到 targetContentOffset
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset{
    //即将滑动停留后展示的indexPath
    NSIndexPath *ip = [self indexPathForRowAtPoint:CGPointMake(0, targetContentOffset->y)];
    //如果滑动过快,这里是之前的值
    NSIndexPath *cip = [[self indexPathsForVisibleRows] firstObject];
    
    NSLog(@"row1 = %zi , row2 = %zi,velocity=%f",ip.row,cip.row,velocity.y);
    NSInteger skipCount = 8;    //以这个值作为开启滑动区域展示
    if (labs(cip.row-ip.row)>skipCount) {
        NSArray *temp = [self indexPathsForRowsInRect:CGRectMake(0, targetContentOffset->y, self.width, self.height)];
        NSMutableArray *arr = [NSMutableArray arrayWithArray:temp];
        if (velocity.y<0) { //多加载后面3个cell,向下滑动
            NSIndexPath *indexPath = [temp lastObject];
            if (indexPath.row+3<datas.count) {
                [arr addObject:[NSIndexPath indexPathForRow:indexPath.row+1 inSection:0]];
                [arr addObject:[NSIndexPath indexPathForRow:indexPath.row+2 inSection:0]];
                [arr addObject:[NSIndexPath indexPathForRow:indexPath.row+3 inSection:0]];
            }
        } else {    //多加载上面3个cell
            NSIndexPath *indexPath = [temp firstObject];
            if (indexPath.row>2) {
                [arr addObject:[NSIndexPath indexPathForRow:indexPath.row-3 inSection:0]];
            }
            if (indexPath.row>1) {
                [arr addObject:[NSIndexPath indexPathForRow:indexPath.row-2 inSection:0]];
            }
            if (indexPath.row>0) {
                [arr addObject:[NSIndexPath indexPathForRow:indexPath.row-1 inSection:0]];
            }
        }
        //得到快速滑动即将停止时候需显示的所有cell
        [needLoadArr addObjectsFromArray:arr];
    }
}

具体参考:https://github.com/johnil/VVeboTableViewDemo

 

人已赞赏
iOS文章

iOS开发之CoreSpotlight框架的应用

2019-9-29 10:02:13

iOS文章

iOS-Object-C的浅拷贝与深拷贝区别

2019-9-29 10:46:58

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
有新消息 消息中心
搜索