iOS TableView的重用机制

2022-11-03  本文已影响0人  高浩浩浩浩浩浩

今天测试反馈了一个问题,有一个列表每次下啦刷新内存都在持续增加,最后因为OOM崩溃了。
这个页面是个双层的TableView,就是外层的每个Cell里面又装了一个TableView。

开始以为是循环引用导致的不释放问题,结果用Instrument查看并没有内存泄漏。

查了差不多一个小时,发现原来是外层的TableView的CellForRows的时候,cell都没有重用,这也就导致每次在CellForRow的方法里面都在持续创建一个新的Cell。又因为新的Cell里面是TableView,所以数据增加的特别快。

那么到这里问题就差不多解决了,用回系统的reuseIdentifier方法。

但是对应的开发说,外层的Cell比较特殊,不能用TableView的重用池管理。
那么对这种情况,直接自己维护一个Cell的重用池好了。

我们创建一个Cell的可变字典:

@property (nonatomic, strong) NSMutableDictionary *cellsDic;


- (NSMutableDictionary *)cellsDic {
      if(!_cellsDic) {
          _cellsDic = [NSMutableDictionary dictionary];
      }
      return _cellsDic;
}

然后我们在cellForRowAtIndexPath:方法里面这么改造:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    BaseCellData *cellData = self.viewModel.dataList[indexPath.row];
    NSString *cellType = [cellData.cellType.identifier stringByAppendingString:[NSString stringWithFormat:@"-%@-%@",@(indexPath.section),@(indexPath.row)]];
    BaseTableViewCell *cell = [self.cellsDic valueForKey:cellType];
    if (!cell) {
          cell = [[DeviceRelevanceTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellType];
          cell.selectionStyle = UITableViewCellSelectionStyleNone;
          [self.cellsDic setValue:cell forKey:cellType];
    }
    [cell updateCellWithData:cellData];
    [cell hideLastCellSeperator:YES];
    return cell;
}

这样子整个TableView的Cell重用池就有我们自己来维护了。

TableView重用的原理

在UITableView头文件中,可以找到NSMutableArray* visiableCells,和NSMutableDictnary* reusableTableCells两个结构。visiableCells内保存当前显示的cells,reusableTableCells保存可重用的cells。

TableView显示之初,reusableTableCells为空,那么tableView dequeueReusableCellWithIdentifier:CellIdentifier返回nil。开始的cell都是通过[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]来创建,而且cellForRowAtIndexPath只是调用最大显示cell数的次数。

实际上TableView并不是只有在滑动过程中才会重用reusableTableCells的表,一些其他情况也会对Cell进行重用:

1. reloadData

这种情况比较特殊。一般是部分数据发生变化,需要重新刷新cell显示的内容时调用。在cellForRowAtIndexPath调用中,所有cell都是重用的。我估计reloadData调用后,把visiableCells中所有cell移入reusableTableCells,visiableCells清空。cellForRowAtIndexPath调用后,再把reuse的cell从reusableTableCells取出来,放入到visiableCells。

2.reloadRowsAtIndex

刷新指定的IndexPath。如果调用时reusableTableCells为空,那么cellForRowAtIndexPath调用后,是新创建cell,新的cell加入到visiableCells。老的cell移出visiableCells,加入到reusableTableCells。于是,之后的刷新就有cell做reuse了。

上一篇 下一篇

猜你喜欢

热点阅读