iOS开发之——UITableView单元格重用问题

引子

iOS开发中,UITableView是一个十分重要的角色,几乎每一个APP都离不开列表。所以列表性能的优化也是日常APP优化的重点。今天在写一个小应用的时候,出现了列表重用错误的情况,总结一下。

UITableViewCell重用机制

关于详细的重用机制,网上一大把,例如这篇文章。总的来说就一个意思:屏幕显示的是固定数目的单元格,可以把推出屏幕的单元格缓存起来,供后续加载使用,既可以加快载入速度,也可以保证内存不会大幅上升。
单元格重用示意图
注:上传完之后才知道图片画错了,其实系统会创建屏幕能显示的cell个数再加1个单元格,也就是多创建一个,图中是单元格6也应该是创建好的,单元格7才是从缓存中读取的。

数据错误现象

也正是因为单元格重用,假如进入缓存的单元格与将要加载的单元格显示的样式不同,则在重用的时候会发生数据错误的情况。例如之前单元格的内容显示在了新的单元格中,或者之前的单元格内容发生改变等等。

解决办法

主要是以下3个方法:

  1. 不重用单元格。很好理解,不过就无法利用单元格重用的种种优点了,基本可以不考虑;
  2. 利用标识符将不同样式的单元格进行标记,然后根据不同的标识符分别进行重用处理;
  3. 在单元格重用后,加载新的数据之前对单元格的内容进行清空。

示例代码

方法2:

NSString *cellIdetifier = [[NSString alloc]init];
if (indexPath.row == 0) //如果是第一行
{
cellIdetifier = @"FirstRowCell";
}
else
{
cellIdetifier = @"NotFirstRowCell";
}
//从缓存池中缓存单元格
HMGHomePageTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdetifier];
if (cell == nil)
{
//如果为空就根据标识符新建
cell = [[HMGHomePageTableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdetifier];
if ([cellIdetifier isEqualToString:@"FirstRowCell"])
{
/**
* 这里根据不同的标识符加载不同的控件即可
*/

}

方法3:

static NSString *cellIdetifier = @"HomePageCell";
HMGHomePageTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdetifier];
if (cell != nil)
{
//清空cell的内容并置nil
[cell.contentView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
cell = nil; //原因见下边注解
}
if (cell == nil)
{
cell = [[HMGHomePageTableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdetifier];
}

注:一开始没有将cell置nil,在执行removeFromSuperview方法的时候,系统会调用layoutSubviews方法,我在这个方法里边进行masonry添加约束,而添加约束的前提是控件一定要先添加到父视图中,绕过init添加到父视图的操作而直接添加约束,会异常,couldn't find a common superView