TableViewcell 复用机制和解决可能因复用引起的问题

TabelViewCell复用机制简述

在tableview新建的时候,会新建一个复用池(reuse pool).这个复用池可能是一个队列,或者是一个链表,保存着当前的Cell.pool中的对象的复用标识符就是reuseIdentifier,标识着不同的种类的cell.所以通常可以利用这点实现下拉到tableview底部后添加一个新的tableviewcell并标示为不同的reuseIdentifier就可以和其他类型的tableviewcell相区分开了。

在UITableView创建同时,会创建一个空的复用池.之后UITableView在内部维护这个复用池.一般情况下,有两种用法,一种是在取出一个空的cell的时候再新建一个.一种是预先注册cell.之后再直接从复用池取出来用,不需要初始化.那么针对这两种不同的复用方式具体说明如下:dequeueReusableCellWithIdentifier:forIndexPath: 和 dequeueReusableCellWithIdentifier:

一、 dequeueReusableCellWithIdentifier:forIndexPath:是iOS 6之后新出的方法,调用时肯定会返回一个Cell,不必使用Cell的 initWithStyle:reuseIdentifier:进行新建,但使用时必须先进行Cell的注册,否则会报错

这种方式在UITableView初始化的时候,注册一个UITableViewCell的类;不用再做空判断;使用此方法之后,就不用再判断取出来的cell是否为空,因为取出来的cell必定存在.也就是替你做了初始化的事情。但是这种方式必须首先注册cell,否则报错,可以通过注册类或者nib文件的形式来进行注册。一般注册的动作放在初始化的地方,例如viewdidload

reason: 'unable to dequeue a cell with identifier Cell - must register a nib or a class for the identifier or connect a prototype cell in a storyboard'

Cell的注册方法:

1.代码创建的Cell注册方法:

- (void)registerClass:(nullableClass)cellClass forCellReuseIdentifier:(NSString*)identifierNS_AVAILABLE_IOS(6_0);

示例:

[_tableView registerClass:[UITableViewCellclass]forCellReuseIdentifier:@"ID"];

2.Xib创建的Cell注册方法:

- (void)registerNib:(nullableUINib*)nib forCellReuseIdentifier:(NSString*)identifierNS_AVAILABLE_IOS(5_0);

示例:

[_tableView registerNib:[UINibnibWithNibName:@"XXXCell"bundle:nil] forCellReuseIdentifier:@"XXXCell"];

3.在StoryBoard上创建的Cell系统会自动进行注册,不需要再注册。

二、dequeueReusableCellWithIdentifier:这个方法使用时可以不进行注册,但调用时返回的值有可能会为空,所以需要在cell的tableView:cellForRowAtIndexPath:方法里需要进行判断返回的值是否为空,如果为空需要调用 initWithStyle:reuseIdentifier:方法进行创建

例如:

- (UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath {UITableViewCell*cell = [tableView dequeueReusableCellWithIdentifier:@"ID"];if(cell ==nil) {        cell = [[UITableViewCellalloc] initWithStyle:UITableViewCellStyleDefaultreuseIdentifier:@"ID"];    }returncell;}

由复用可能引起的展示问题

UITableView中的cell可以有很多,一般会通过重用cell来达到节省内存的目的:通过为每个cell指定一个重用标识符(reuseIdentifier),即指定了单元格的种类,当cell滚出屏幕时,会将滚出屏幕的单元格放入重用的queue中,当某个未在屏幕上的单元格要显示的时候,就从这个queue中取出单元格进行重用。

但对于多变的自定义cell,有时这种重用机制会出错。比如,当一个cell含有另外一个cell不包含的控件内容,当复用机制起作用时,可能正好读到有额外控件的cell从而展示出来,造成界面上的错乱,这是很常见的问题。

网上针对这个问题往往无外乎两种方式,一个弃用重用机制,一个清除复用cell的内容。

首先就对常用的方式做一简单总结:

1. 弃用重用机制 - 从indexpath每次获取新的cell

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {    

   UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];

if(cell == nil) {

        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];

    }

}

2.弃用重用机制 - 为每一个cell设置不同的重用id,因为tableview只有相同重用id的cell才会被重用,实质上也是舍弃了重用

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{

    NSString *CellIdentifier = [NSString stringWithFormat:@"Cell%d%d", [indexPath section], [indexPath row]];//以indexPath来唯一确定cell UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];//出列可重用的cell if(cell == nil) {

        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];

    }

    //...其他代码 }

3. 清除重用cell的所有的子控件 (这种方式不能通过注册nib方式,只能自定义view中复写其中的init方法从而达到效果)

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

{

    staticNSString *CellIdentifier =@"Cell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];//出列可重用的cell 

if(cell == nil) {

        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];

    }

    else

    {

        //删除cell的所有子视图 while([cell.contentView.subviews lastObject] != nil)

        {

            [(UIView*)[cell.contentView.subviews lastObject] removeFromSuperview];

        }

    }

    //...其他代码 }

view部分的代码

TableViewcell 复用机制和解决可能因复用引起的问题_第1张图片
TableViewcell 复用机制和解决可能因复用引起的问题_第2张图片

4. 清除重用子控件的内容 (支持代码或者nib文件的实现)

    [self.tableView registerNib:[UINib nibWithNibName:@"DownloadImageTableViewCell" bundle:[NSBundle mainBundle]] forCellReuseIdentifier:@"downloadImageCell"];

- (UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath {

    DownloadImageTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"downloadImageCell" forIndexPath:indexPath];

    //清除所有的内容而不是控件

    [cellclearContents];

//其他操作

    return cell;

}

5.通过tableview对应的复用方法来做内容的清空 (个人推荐,比较优雅达到清除界面信息的目的)

cell被重用如何提前知道? 重写cell的prepareForReuse官方头文件中有说明.当前已经被分配的cell如果被重用了(通常是滚动出屏幕外了),会调用cell的prepareForReuse通知cell.注意这里重写方法的时候,注意一定要调用父类方法[super prepareForReuse] .这个在使用cell作为网络访问的代理容器时尤其要注意,需要在这里通知取消掉前一次网络请求.不要再给这个cell发数据了.

// if the cell is reusable (has a reuse identifier), this is called just before the cell is returned from the table view method dequeueReusableCellWithIdentifier:.  If you override, you MUST call super.

- (void)prepareForReuse {

[super prepareForReuse];

}

代码实例:

TableViewcell 复用机制和解决可能因复用引起的问题_第3张图片
TableViewcell 复用机制和解决可能因复用引起的问题_第4张图片

自定义UITableViewCell的方法有很多 发现一些人都会遇到自己定义的cell里面图片错乱的问题 这个问题往往是因为没有实现prepareForReuse这个方法导致的.

UITableViewCell在向下滚动时复用, 得用的cell就是滑出去的那些, 而滑出去的cell里显示的信息就在这里出现了 解决的方法就是在UITableViewCell的子类里实现perpareForReuse方法, 把内容清空掉

在后面的文章中会针对不同的创建tableview的方式对体现的复用和去除复用机制做以详细讲解。

你可能感兴趣的:(TableViewcell 复用机制和解决可能因复用引起的问题)