你真的了解UITableViewCell重用吗?

一:首先查看一下关于UITableViewCell重用的定义git

- (nullable __kindof UITableViewCell *)dequeueReusableCellWithIdentifier:(NSString *)identifier;  

- (__kindof UITableViewCell *)dequeueReusableCellWithIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(6_0);

在tableview新建的时候,会新建一个复用池(reuse pool).这个复用池多是一个队列,或者是一个链表,保存着当前的Cell.pool中的对象的复用标识符就是reuseIdentifier,标识着不一样的种类的cell.因此调用dequeueReusableCellWithIdentifier:方法获取cell.从pool中取出来的cell都是tableview展现的原型.不管以前有什么状态,所有都要设置一遍.github

UITableView建立同时,会建立一个空的复用池.以后UITableView在内部维护这个复用池.通常状况下,有两种用法,一种是在取出一个空的cell的时候再新建一个.一种是预先注册cell.以后再直接从复用池取出来用,不须要初始化.微信

第一种方法:网络

- (nullable __kindof UITableViewCell *)dequeueReusableCellWithIdentifier:(NSString *)identifier;  

UITableview第一次执行tableView:cellForRowAtIndexPath:.当前复用池为空.dequeueReusableCellWithIdentifier调用中取出cell,并检测cell是否存在.目前并不存在任何cell,因而新建一个cell,执行初始化, 并return cell.ide

代码以下:this

#define kInputCellReuseIdentifierPWD   @"password_cell"
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kInputCellReuseIdentifierPWD];
if (!cell) {
    cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:kInputCellReuseIdentifierPWD];
}

cell.textLabel.text = @"It is awesome";
return cell;

上面的代码中,你返回的cell会被UITableView添加到复用池中.第二次调用tableView:cellForRowAtIndexPath:,当前复用池中有一个cell.这时候由于UITableView上面还未填满,并且复用池中惟一的那一个已经在使用了.因此取出来的Cell仍然是nil.因而继续新建一个cell并返回,复用池再添加一个cell,当前复用池中cell的个数为2.假如当前tableview只能容纳5个cell.那么在滚动到第6个cell时,从tableview的复用池取出来的cell将会是第0行的那个cell.以此类推,当滚动到第7行时,会从复用池取出来第1行的那个cell. 另外,此时再也不继续往复用池添加新的cell.spa

第二种方法:代理

- (__kindof UITableViewCell *)dequeueReusableCellWithIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(6_0);

在UITableView初始化的时候,注册一个UITableViewCell的类;不用再作空判断;code

[tableView registerClass:[BLSProjectMoneyCompleteCell class] forCellWithReuseIdentifier:NSStringFromClass([BLSProjectMoneyCompleteCell class])];

使用此方法以后,就不用再判断取出来的cell是否为空,由于取出来的cell一定存在.调用dequeueReusableCellWithIdentifier:方法时,会先判断当前复用池时候有可用复用cell.若是没有,tableview会在内部帮咱们新建一个cell,其余的跟方法一同样orm

BLSProjectMoneyCompleteCell *cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([BLSProjectMoneyCompleteCell class])];

二:UITableViewCell重用时引起的问题

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

但对于多变的自定义cell,有时这种重用机制会出错。好比,当一个cell含有一个UITextField的子类并被放在重用queue中以待重用,这时若是一个未包含任何子视图的cell要显示在屏幕上,就会取出并使用这个重用的cell显示在无任何子视图的cell中,这时候就会出错。

方法一:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    static NSString *CellIdentifier = @"Cell"; 
    // UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; //改成如下的方法 
    UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath]; //根据indexPath准确地取出一行,而不是从cell重用队列中取出 
    if (cell == nil) { 
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]; 
    } 
     //...其余代码                               
} 

将得到cell的方法从- (UITableViewCell*)dequeueReusableCellWithIdentifier:(NSString*)identifier 换为-(UITableViewCell *)cellForRowAtIndexPath:(NSIndexPath *)indexPath;重用机制调用的就是dequeueReusableCellWithIdentifier这个方法,方法的意思就是“出列可重用的cell”,于是只要将它换为cellForRowAtIndexPath(只从要更新的cell的那一行取出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]; 
    } 
    //...其余代码 
} 

经过为每一个cell指定不一样的重用标识符(reuseIdentifier)来解决。重用机制是根据相同的标识符来重用cell的,标识符不一样的cell不能彼此重用。因而咱们将每一个cell的标识符都设置为不一样,就能够避免不一样cell重用的问题了。

方法三:


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    static NSString *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]; 
        } 
    } 
    //...其余代码 
}

删除重用cell的全部子视图;这个方法是经过删除重用的cell的全部子视图,从而获得一个没有特殊格式的cell,供其余cell重用。考虑到内存问题,cell少得时候能够每一个都添加标识符,当cell重用较多时,考虑内存问题,建议用删除cell的全部子视图方法(作视频播放的时候).

三: 有时Assertion failure in dequeueReusableCellWithIdentifier:forIndexPath: 闪退问题

在先前作的分组列表中,两组的Cell是不同样式,两组分别运用以下的代码(也有在初始化注册Cell),在IOS9如下会闪退

static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier 
                                     forIndexPath:indexPath];

后来修改为:

static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

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

 四:prepareForReuse方法去重的效果

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];
}

自定义UITableViewCell的方法有不少 发现一些人都会遇到本身定义的cell里面图片错乱的问题 这个问题每每是由于没有实现prepareForReuse这个方法致使的.

UITableViewCell在向下滚动时复用, 得用的cell就是滑出去的那些, 而滑出去的cell里显示的信息就在这里出现了 解决的方法就是在UITableViewCell的子类里实现perpareForReuse方法, 把内容清空掉,就能够对控件进行清空,实现被重复增长的问题;

-(void)prepareForReuse
{
   [super prepareForResuse];

    _titleLb.text = nil;
    _commentLabel.text = nil;
    _standarLabel.text = nil;
    _photos = nil;

    _line1.hidden = YES;
    _line2.hidden = YES;
    for (UIView *view in _imgViews.subviews) {
        if (view.tag >0 && ([view isKindOfClass:[UIImageView class]]||[view isKindOfClass:[UILabel class]])) {
            [view removeFromSuperview];
        }
    }
}

 

 

最近有个妹子弄的一个关于扩大眼界跟内含的订阅号,天天都会更新一些深度内容,在这里若是你感兴趣也能够关注一下(嘿对美女跟知识感兴趣),固然能够关注后输入:github 会有个人微信号,若是有问题你也能够在那找到我;固然不感兴趣无视此信息;

相关文章
相关标签/搜索