美文网首页iOS开发攻城狮的集散地
iOS 列表遇到复杂 cell 时的加载优化

iOS 列表遇到复杂 cell 时的加载优化

作者: Nagi | 来源:发表于2017-12-27 17:48 被阅读60次

    在同一个 UITableView 中显示不同样式的复杂 Cell 时,必然会遇到这么一个问题:虽然有重用机制,但在首次创建的时候,还是会消耗不少cpu时间,造成卡顿。

    举个例子:UITableView 有两类Cell,初始显示A类,那么切换到B类的时候,就必定要创建B类Cell,造成卡顿,虽然创建完之后就没有卡顿问题了(前提是做好一系列其它的优化),但还是有点不爽,那么该怎么解决这个问题呢?

    我想到3个方案:

    • AsyncDisplayKit,这个之前使用过,虽然没有卡顿,但是UI的展示会有延迟,有些本来不会卡顿的场景也会有UI延迟的问题,有它的使用场景,但放到这里来,感觉并不是完美的方案,而且有种杀鸡用牛刀的感觉。
    • 在同一种 Cell 中通过隐藏部分UI元素避免额外的新建,这种方案的弊端是Cell类实现起来必定巨大不堪,难以维护,相比得到的好处,弊多利少。
    • 提前创建好足够使用的 Cell 缓存池,然后在要使用的地方先从自己之前创建好的地方去取,这样其实是将创建提前,虽然开始时加载时间延长了,但是使用时候就不会有卡顿了。而且修改量也小,实现起来比较容易。

    取第三种方案,看代码,实现起来还是稍微有一些注意点的:

    ···
    // p_speedUpCellCreate 函数,每种Cell要创建的数量可以根据不同类型Cell的高度和手机型号预估,太少则不够,太多则太耗时

    // 因为使用了 FDTemplateLayoutCell 在计算高度的时候会私藏一份,所以提前调用创建
    [self.tableView fd_heightForCellWithIdentifier:kIdentifierA configuration:nil];
    [self.tableView fd_heightForCellWithIdentifier:kIdentifierB configuration:nil];
    
    // 不能直接new,需要指定重用标识符
    UITableViewCellA* cellA =  [[UITableViewCellA alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:kIdentifierA];
    // 因为使用了 MGSwipeButton ,所以要提前创建,每次仅更新 Buttons,不创建新的 Button
    cell.rightButtons = [self p_configCellRightButtons:0 buttons:[cell.rightButtons mutableCopy]];
    // 存起来
    [self.cellSpeedUp[kIdentifierA] addObject: cellA];
    
    ...
    UITableViewCellB* CellB = .....
    
    
    然后在 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRow:(NSInteger)row 中
    
    UITableViewCell* cell = nil;
    if (self.cellSpeedUp[kIdentifierA].count > 0) {
        cell = self.cellSpeedUp[kIdentifierA][0];
        [self.cellSpeedUp[kIdentifierA]] removeObjectAtIndex:0];
    }
    else
    {
        cell = [tableView dequeueReusableCellWithIdentifier: kIdentifierA];
    }
    

    ···

    然后将p_speedUpCellCreate 放到合适的地方去调用就行了,当然必须放在主线程。


    2018-04-04 更新

    随着需求的发展,逐渐出现新的隐患:随着cell种类的增多,缓存池创建的时间也逐渐增多,比如原来3种,每种创建6个,那就是18个,如果种类增加到10种,缓存池的数量剧增到60个。虽然目前耗时不太明显,但可以预见到的是早晚有一天时间会不可接受。那么,如何优化呢?

    思路1

    能不能将创建时间从启动延迟到启动后?
    尝试了一下,利用GCD的异步低优先级队列,将创建延迟到了启动完成后,实际效果为启动变快了,但会有短暂的界面无响应情况存在(主线程正在创建cell)。配合页面下拉刷新的动画,貌似能接受。
    但总还是不完美,是吧?

    思路2

    其实问题在于创建了太多用不着的cell,可能实际用户只有一种cell,那么其实就是做了很多多余的动作,想到这一点,就有办法了。
    理想的解决方案是后台提供一个接口,返回各种 数据的数量(如果有分页,只需第一页的数量即可),按照这个数量进行缓存池的创建即可。
    不依赖后台的情况下,可以在每次拉取数据之后,将各种类型的数量进行缓存,下次启动时,根据不同类型不同分类计算恰当的缓存数量。

    补充优化

    进一步还可以根据不同机型 ,不同类别的cell,设置预估单屏数量,如果cell高度比较高,可能两三个就够了,如果高度比较低,那么需要多一点。

    后记

    经过这些优化,可以将cell缓存池单来的副作用降低到最低,后期将不会产生cell种类增多带来启动时间增加的影响。

    相关文章

      网友评论

        本文标题:iOS 列表遇到复杂 cell 时的加载优化

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