在同一个 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种类增多带来启动时间增加的影响。
网友评论