美文网首页
UITableView优化思路与复用

UITableView优化思路与复用

作者: 代码堆在那_我往哪栈 | 来源:发表于2016-09-11 09:36 被阅读431次

    单元格复用问题    demo 点击此处下载

    UITableView最核心的思想就是UITableViewCell的重用机制。简单的理解就是:UITableView一开始只会创建屏幕上要显示的Cell,每当Cell滑出屏幕时,就会放入到一个叫“重用池”的里面,滑动中当要显示某一位置的Cell时,会先去“重用池”取,如果有,就直接拿来显示;如果没有,才会创建。这样做的好处就是极大的减少了内存的开销,不需要每个都创建了。那么我们首先对复用进行一个探究(以下探究方法是当初刚学习OC时候网上受到的启发,自己按照这个思路尝试)。


    // 方法1

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"qwer"];

    if (!cell) {

    cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"qwer"];

    }


    // 方法2(xib),在GLImageCell.xib文件中identifier中输入GLImageCell,在GLImageCell.m中复写initWithStyle reuseIdentifier方法  复写内容见demo(loadNibNamed的方法)

    GLImageCell *cell = [tableView dequeueReusableCellWithIdentifier:@"GLImageCell"];

    if (nil == cell) {

    cell = [[GLImageCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"GLImageCell"];

    }


    // 方法3

    Method3Cell *cell = [tableView dequeueReusableCellWithIdentifier:@"method3"];

    if (nil == cell) {

    cell = [[[NSBundle mainBundle]loadNibNamed:@"Method3Cell" owner:self options:nil]lastObject];

     cell.selectionStyle=UITableViewCellSelectionStyleNone;

     [tableView registerNib:[UINib nibWithNibName:@"Method3Cell" bundle:[NSBundle mainBundle]] forCellReuseIdentifier:@"method3"];

    }

    return cell;


    // 方法4

    1.在viewdidload方法里面

    UINib *firstNib = [UINib nibWithNibName:@"Method3Cell" bundle:nil];

    [_tableView registerNib:firstNib forCellReuseIdentifier:@"Method3"];

    2.在 dequeueReusableCellWithIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath *)indexPath方法中

    Method3Cell *cell = [tableView dequeueReusableCellWithIdentifier:@"Method3" forIndexPath:indexPath];


    //在以下方法中这么写

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

    static int myCount = 0;       //static变量只是在编译时候进行初始化

    TableViewCell *cell;

    cell = [tableView dequeueReusableCellWithIdentifier:@"test"];

    if (nil == cell) {

    // 此处调用方法1,2,3,4进行测试

    myCount ++;

    NSLog(@"count :%d ",myCount);

    }

    return cell;

    }

    测试输出结果如下

    //方法1  输出为1,2,3,4,5,6,7,8,9   

    //方法2  输出为1,2,3,4,5,6,7,8,9

    //方法3  输出为1

    //方法4  输出为               (就是不输出)


    为什么会这样?首先我们大胆猜测一下是不是这样的:方法1和方法2是因为一开始都没有对应这个Identifier的cell,所以每次都进入if(cell==nil)方法里面。方法3是因为第一次没有找到对应Identifier的cell,于是进入if(cell==nil)里面,然后调用的注册方法,所以只输出一个数。方法4是因为在最开始就已经注册过,所以不会进入if里面,所以就没有输出。

    我们先来看这句 dequeueReusableCellWithIdentifier 是什么意思,我们查看官方文档

    网上翻译一下大致意思:

    出于性能的原因,一个表视图的数据源应该采用可复用的表视图单元对象。一个表视图维护着一个可复用单元的队列或者列表。当要显示一个新的单元的时候就调用这个方法,这个方法会出列一个已经存在的单元。假如没有可以复用的单元那么就返回nil。

    从表视图的生命周期来说,一开始可复用队列为空,调用dequeueReusableCellWithIdentifier:肯定返回nil。然后就调用initWithStyle:reuseIdentifier:方法来产生并且标识复用记号的表视图单元。满屏显示的时候,滚动表视图,一侧的单元就会被移出屏幕,此时这个单元进入可复用单元队列,然后调用prepareForReuse方法准备一个即将出列的单元, dequeueReusableCellWithIdentifier:从可复用单元队列里出列一个可复用单元。

    //修改一下例子

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

    static int myCount = 0;      //static变量只是在编译时候进行初始化

    TableViewCell *cell;

    cell = [tableView dequeueReusableCellWithIdentifier:@"test"];

    if (nil == cell) {

    // 此处调用方法1,2,3,4进行测试

    myCount ++;

    NSLog(@"count :%d ",myCount);

    }

    //第二种输出

    NSLog(@"+++++++++before:%@",cell.textLabel.text);

    cell.textLabel.text = [NSString stringWithFormat:@"%ld",(long)indexPath.row];

    NSLog(@"---------after:%@",cell.textLabel.text);

    输出结果

    /* 方法3的第二种输出

    2016-09-08 16:49:18.066 TableViewTest[43861:2739078] time : 1

    2016-09-08 16:49:18.068 TableViewTest[43861:2739078] +++++++++before:(null)

    2016-09-08 16:49:18.068 TableViewTest[43861:2739078] ---------after:0

    2016-09-08 16:49:18.071 TableViewTest[43861:2739078] +++++++++before:(null)

    2016-09-08 16:49:18.071 TableViewTest[43861:2739078] ---------after:1

    2016-09-08 16:49:18.072 TableViewTest[43861:2739078] +++++++++before:(null)

    2016-09-08 16:49:18.072 TableViewTest[43861:2739078] ---------after:2

    2016-09-08 16:49:18.072 TableViewTest[43861:2739078] +++++++++before:(null)

    2016-09-08 16:49:18.073 TableViewTest[43861:2739078] ---------after:3

    2016-09-08 16:49:18.073 TableViewTest[43861:2739078] +++++++++before:(null)

    2016-09-08 16:49:18.073 TableViewTest[43861:2739078] ---------after:4

    2016-09-08 16:49:18.074 TableViewTest[43861:2739078] +++++++++before:(null)

    2016-09-08 16:49:18.098 TableViewTest[43861:2739078] ---------after:5

    2016-09-08 16:49:18.099 TableViewTest[43861:2739078] +++++++++before:(null)

    2016-09-08 16:49:18.099 TableViewTest[43861:2739078] ---------after:6

    2016-09-08 16:49:18.100 TableViewTest[43861:2739078] +++++++++before:(null)

    2016-09-08 16:49:18.100 TableViewTest[43861:2739078] ---------after:7

    2016-09-08 16:49:18.101 TableViewTest[43861:2739078] +++++++++before:(null)

    2016-09-08 16:49:18.101 TableViewTest[43861:2739078] ---------after:8

    2016-09-08 16:49:19.966 TableViewTest[43861:2739078] +++++++++before:(null)

    2016-09-08 16:49:19.966 TableViewTest[43861:2739078] ---------after:9

    2016-09-08 16:49:32.542 TableViewTest[43861:2739078] +++++++++before:(null)

    2016-09-08 16:49:32.543 TableViewTest[43861:2739078] ---------after:10

    2016-09-08 16:49:37.408 TableViewTest[43861:2739078] +++++++++before:1

    2016-09-08 16:49:37.409 TableViewTest[43861:2739078] ---------after:11

    */

    /*方法1,方法2,方法4的第二种输出

    2016-09-08 16:53:31.731 TableViewTest[43916:2742025] time : 1

    2016-09-08 16:53:31.734 TableViewTest[43916:2742025] +++++++++before:(null)

    2016-09-08 16:53:31.735 TableViewTest[43916:2742025] ---------after:0

    2016-09-08 16:53:31.736 TableViewTest[43916:2742025] time : 2

    2016-09-08 16:53:31.736 TableViewTest[43916:2742025] +++++++++before:(null)

    2016-09-08 16:53:31.737 TableViewTest[43916:2742025] ---------after:1

    2016-09-08 16:53:31.737 TableViewTest[43916:2742025] time : 3

    2016-09-08 16:53:31.738 TableViewTest[43916:2742025] +++++++++before:(null)

    2016-09-08 16:53:31.738 TableViewTest[43916:2742025] ---------after:2

    2016-09-08 16:53:31.738 TableViewTest[43916:2742025] time : 4

    2016-09-08 16:53:31.738 TableViewTest[43916:2742025] +++++++++before:(null)

    2016-09-08 16:53:31.739 TableViewTest[43916:2742025] ---------after:3

    2016-09-08 16:53:31.739 TableViewTest[43916:2742025] time : 5

    2016-09-08 16:53:31.740 TableViewTest[43916:2742025] +++++++++before:(null)

    2016-09-08 16:53:31.740 TableViewTest[43916:2742025] ---------after:4

    2016-09-08 16:53:31.740 TableViewTest[43916:2742025] time : 6

    2016-09-08 16:53:31.741 TableViewTest[43916:2742025] +++++++++before:(null)

    2016-09-08 16:53:31.741 TableViewTest[43916:2742025] ---------after:5

    2016-09-08 16:53:31.742 TableViewTest[43916:2742025] time : 7

    2016-09-08 16:53:31.742 TableViewTest[43916:2742025] +++++++++before:(null)

    2016-09-08 16:53:31.742 TableViewTest[43916:2742025] ---------after:6

    2016-09-08 16:53:31.743 TableViewTest[43916:2742025] time : 8

    2016-09-08 16:53:31.743 TableViewTest[43916:2742025] +++++++++before:(null)

    2016-09-08 16:53:31.744 TableViewTest[43916:2742025] ---------after:7

    2016-09-08 16:53:31.744 TableViewTest[43916:2742025] time : 9

    2016-09-08 16:53:31.745 TableViewTest[43916:2742025] +++++++++before:(null)

    2016-09-08 16:53:31.745 TableViewTest[43916:2742025] ---------after:8

    2016-09-08 16:53:38.519 TableViewTest[43916:2742025] time : 10

    2016-09-08 16:53:38.519 TableViewTest[43916:2742025] +++++++++before:(null)

    2016-09-08 16:53:38.519 TableViewTest[43916:2742025] ---------after:9

    2016-09-08 16:53:38.604 TableViewTest[43916:2742025] +++++++++before:0

    2016-09-08 16:53:38.605 TableViewTest[43916:2742025] ---------after:10

    2016-09-08 16:53:38.688 TableViewTest[43916:2742025] +++++++++before:1

    2016-09-08 16:53:38.689 TableViewTest[43916:2742025] ---------after:11

    2016-09-08 16:53:38.740 TableViewTest[43916:2742025] +++++++++before:2

    2016-09-08 16:53:38.740 TableViewTest[43916:2742025] ---------after:12

    2016-09-08 16:53:38.808 TableViewTest[43916:2742025] +++++++++before:3

    2016-09-08 16:53:38.808 TableViewTest[43916:2742025] ---------after:13

    */

    方法3中,可复用的cell的text从1开始,从xib中创建的第一个cell,它的identify还没有注册,所以identify值没有,不能复用。复用是从第二个单元格开始

    方法1,方法2,方法4中,可复用的cell从第一个单元格开始。

    以上的结果跟我们一开始的猜测应该是一样的吧!

    在标题边上有github的demo下载地址,有需要的朋友可以下载来看一下   demo下载


    UITableView优化问题   

    在实际开发中,有时候只使用单元格复用还是不够的,在滑动的时候也同样会有卡顿感,那么我们该怎么办呢?

    以下是天哥根据多年网上的学习归纳总结的几点,不对的地方大家也请点出来:

    1.单元格复用

    这个我们前面探究的时候说过了,单元格复用能有效的减少内存的消耗。单元格复用的实现是最简单也是最重要的。

    2.使用异步网络请求,缓存图片或者数据

    如果我们要显示的内容跟之前的一样,每次都要从网上获取,是不是很浪费呢?我们要减少数据加载和逻辑处理的时间,从内存中获取就大大减少了这方面的消耗,我们要将。现在被大家广泛使用的AFNetworking就是异步网络请求。

    3. 提前计算并缓存Cell的高度

    在UITableView中,运行时首先调用的方法是”tableView:heightForRowAtIndexPath:”,如果我们在这个方法里面进行各种计算什么的,将会大大增加手机的负担。所以,我们尽量能不调用代理方法的就不要调用(在创建tableview的时候使用tableview.rowHeight来确定cell的高度)。必须调用的情况下(比如每个section所对应的cell不一样等等)也尽量不要在”tableView:heightForRowAtIndexPath:”这个方法中进行一系列的计算,而是提前计算好,直接将计算好的值使用到这个方法中。如果必须要在方法里面计算,那要尽力做最简单的计算。

    4.异步绘制

    在自定义的cell中,假设工作需求里面,我们要将cell上的某个Button的边框变成红色并且边框要有一点圆弧形。这个时候,我们对view外形的操作最好都放在类似于drawRect的方法里面。drawRect方法是异步绘制,可以大大的缓解主线程的压力。

    5.滑动时,按需加载。图片加载时,异步加载(SDWebImage已经实现了异步加载图片,有兴趣的朋友可以去研究一下)。

    大家考虑一个场景,如果我们滑动的很快,中间那些瞬间而过的内容是不是不需要显示?哪怕显示了也没用对吧?滑动的那么快我们怎么看得清呢?所以,按需加载就很重要了。我举的例子只是其中的一种情况,这里的“需”每个人都各不一样,有些可能是希望当滑动停止的时候才加载图片(如安卓版本的饿了么。但是iOS版本的饿了么并没有做任何处理,版本低点的iphone手机就会有卡顿感)还有百度外卖的滑动显示逻辑又不一样了(安卓版本的百度外卖在滑动时,要显示下一个图片就慢慢加载出来。iOS版本就没做这个处理,同样老一点的iphone手机也有卡顿感)这里卡顿感我都是用iphone5体验到的,我用iphone6s点外卖就没有卡顿感。按需加载最重要的一点是,我们充分利用好以下方法(并不绝对,看每个人的需求)

    - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView

    - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity

    - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate

    - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView

    这些方法思想就是识别UITableView禁止或者减速滑动结束的时候,进行异步加载图片,快滑动过程中,只加载目标范围内的Cell,这样按需加载,极大的提高流畅度。而SDWebImage可以实现异步加载,与这条性能配合就完美了,尤其是大量图片展示的时候。

    6.尽量减少subviews的个数和层级

    有时候根据需求,我们不得不在cell上面添加button,image,label,textview等等。必须要加的东西,我们还是要加,但是我们尽量想办法减少这些层级数量。如果我们对某个view设置了alpha < 1,我们应该将这个view的opaque属性设置为NO。如果alpha = 1,则设置opaque为YES。默认情况下opaque为YES。  原因见官方文档,英语大致意思相对简单,我就不翻译了。

     

    关于优化tableview的demo是我在以前学习网上知识下的demo上进行了修改并且增加了注释后完成的,便于大家理解。优化方法很多,这并不是最好的优化,但是可以给大家做一个参考。      优化demo下载     

    感谢大家的支持,zty能力有限。也感谢那些网上给予参考和启发的文章。

    相关文章

      网友评论

          本文标题:UITableView优化思路与复用

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