UITableView 复用机制

复用机制

UITableView 首先加载可以覆盖一屏幕的 UITableViewCell(具体个数要根据每一个 cell 的高度而定)。ui

而后当咱们往上滑动时(往下滑动同理),须要一个新的 cell 放置在列表的下方。此时,咱们不去生成新的 cell 而是先从 UITableView 的复用池里去获取,该池存放了已经生成的可以复用的 cell ,若是该池为空,才会主动建立一个新的 cell 。spa

复用池的 cell 是这样被添加至该池的:当咱们向上滑动视图时(向下滑动同理),位于最顶部的 cell 会相应地往上运动,直至消失在屏幕上。当其消失在视图中时,会被添加至当前 UITableView 的复用池。code

所以,在渲染海量数据的列表时,并不须要不少 cell ,这得益于 UITableView复用机制orm

存在问题

在使用 UITableView 时,咱们可使用 dequeueReusableCellWithIdentifier: 方法实现 cell 实例的复用。内存

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // 定义 cell 标识。
    static NSString *CellIdentifier = @"Cell";

    // 从复用池获取 cell 实例(可能为空)。
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];


    if (!cell) {
        cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
    }

    // 对 cell 进行简单地数据配置.
    // 偶数行的 title 文字颜色设置为红色。
    if (indexPath.row % 2 == 0) {
        [cell.titleLabel setTextColor:[UIColor redColor]];
        cell.titleLabel.text = @"Title of Even Row";
    } else {
        cell.detailLabel.text = @"Detail";
    }

   return cell;
}
复制代码

在上述代码中,咱们但愿偶数行的标题颜色设置为红色、标题内容为 "Title of Even Row"、详情内容为空,但愿奇数行的标题为空、详情内容为 "Detail" 。开发

可是,当咱们滑动列表时,发现样式错乱了:偶数行的详情内容不为空、奇数行的标题不为空。rem

该问题存在的根源在于 cell 实例的复用机制:当咱们没有显式地设置 cell 的样式和内容时,它会继续沿用回收前的样式和内容设置string

解决办法

面对样式错乱的问题,咱们有这样一种解决方法:在从复用池得到一个 cell 实例时,咱们须要显示地设置它的全部样式和内容。该方法确实可以解决问题,但对于开发人员来讲,心智压力是巨大的。it

咱们须要“心智压力不那么大”的解决方法。io

方案一

方案描述:取消 cell 的复用机制,每次渲染都选择建立新的 cell 实例,将原有的 dequeueReusableCellWithIdentifier: 方法替换为 cellForRowAtIndexPath:

缺点:没法复用 cell ,在列表项较多时存在内存占用过大的问题。

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // 定义 cell 标识。
    static NSString *CellIdentifier = @"cell";

    // 经过 indexPath 建立 cell 实例,使得对于不一样的 indexPath ,
    // 其对应的 cell 实例是不一样的,从而解决问题。
    UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];

    if (!cell) {
        cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
    }

    // 对 cell 进行简单地数据配置.
    // 偶数行的 title 文字颜色设置为红色。
    if (indexPath.row % 2 == 0) {
        [cell.titleLabel setTextColor:[UIColor redColor]];
        cell.titleLabel.text = @"Title of Even Row";
    } else {
        cell.detailLabel.text = @"Detail";
    }

    return cell;
}
复制代码

方案二

方案描述:为每一个 cell 根据 indexPath 建立惟一的标识符

缺点:虽然可复用 cell ,但在列表项较多时仍存在内存占用过大的问题。

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // 根据 indexPath 定义 cell 标识。
    NSString *CellIdentifier = [NSString stringWithFormat:@"cell%ld%ld",indexPath.section,indexPath.row];

    // 从复用池获取 cell 实例(可能为空)。
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    if (!cell) {
        // 当复用池获取的 cell 实例为空时,须要建立新的 cell 实例。
        cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
    }

    // 对 cell 进行简单地数据配置.
    // 偶数行的 title 文字颜色设置为红色。
    if (indexPath.row % 2 == 0) {
        [cell.titleLabel setTextColor:[UIColor redColor]];
        cell.titleLabel.text = @"Title of Even Row";
    } else {
        cell.detailLabel.text = @"Detail";
    }

    return cell;
}
复制代码

方案三

方案描述:每从复用池得到一个 cell 实例时,当其不为空时,咱们须要删除其全部的子视图

该方案可实现 cell 实例的复用,成功解决了问题,应该是最好的解决方案

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // 定义 cell 标识。
    static NSString *CellIdentifier = @"Cell";

    // 从复用池获取 cell 实例(可能为空)。
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    if (!cell) {
        // 当复用池获取的 cell 实例为空时,须要建立新的 cell 实例。
        cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
    } else {
        // 当复用池获取的 cell 实例不为空时,删除其全部子视图,使其变“干净”。
        while ([cell.contentView.subviews lastObject] != nil) {
            [(UIView *)[cell.contentView.subviews lastObject] removeFromSuperview];
        }
    }

    // 对 cell 进行简单地数据配置.
    // 偶数行的 title 文字颜色设置为红色。
    if (indexPath.row % 2 == 0) {
        [cell.titleLabel setTextColor:[UIColor redColor]];
        cell.titleLabel.text = @"Title of Even Row";
    } else {
        cell.detailLabel.text = @"Detail";
    }

    return cell;
}
复制代码
相关文章
相关标签/搜索