你真的了解UITableViewCell重用吗?

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

- (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展示的原型.无论之前有什么状态,全部都要设置一遍.

在UITableView创建同时,会创建一个空的复用池.之后UITableView在内部维护这个复用池.一般情况下,有两种用法,一种是在取出一个空的cell的时候再新建一个.一种是预先注册cell.之后再直接从复用池取出来用,不需要初始化.

第一种方法:

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

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

代码如下:

#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.

第二种方法:

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

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

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

使用此方法之后,就不用再判断取出来的cell是否为空,因为取出来的cell必定存在.调用dequeueReusableCellWithIdentifier:方法时,会先判断当前复用池时候有可用复用cell.如果没有,tableview会在内部帮我们新建一个cell,其他的跟方法一一样

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];
    }
时间: 2024-10-23 21:42:29

你真的了解UITableViewCell重用吗?的相关文章

UITableViewCell重用机制

1.UITableViewCell重用机制    1.cell使用重用的原因和重用机制的原理:      原因:     一个UITableView中有许多需要显示的cell,但是我们不可能每个都会浏览到,那么如果我们把这些数据全部都加载进去,是会造成内存的负担的.     我们所能显示的区域通常只有一个屏幕的大小,那么那些屏幕之外的信息是不需要一次性全都加载完的,只有当我们滑动屏幕需要浏览的时候,我们才需要它加载进来.因此,就有了我们要介绍的这部分内容,UITabelViewCell的重用机制

UITableviewCell 重用内存

转载自:http://www.cnblogs.com/tangbinblog/p/3371545.html 重用实现分析 查看UITableView头文件,会找到NSMutableArray*  visiableCells,和NSMutableDictnery* reusableTableCells两个结构.visiableCells内保存当前显示的cells,reusableTableCells保存可重用的cells. TableView显示之初,reusableTableCells为空,那么

UITableViewCell重用的问题

UITableView中有两种重用Cell的方法: Ios代码   - (id)dequeueReusableCellWithIdentifier:(NSString *)identifier; - (id)dequeueReusableCellWithIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(6_0); 在iOS 6中dequeueReusableCellWi

UITableViewCell 重用原理(reuse)

iOS设备的内存有限,如果用UITableView显示成千上万条数据,就需要成千上万个UITableViewCell对象的话,那将会耗尽iOS设备的内存.要解决该问题,需要重用UITableViewCell对象 重用原理:当滚动列表时,部分UITableViewCell会移出窗口,UITableView会将窗口外的UITableViewCell放入一个对象池中,等待重用.当UITableView要求dataSource返回UITableViewCell时,dataSource会先查看这个对象池,

iOS中UITableViewCell的重用问题解决方案

UITableViewCell重用 为了能够保证tableViewCell能够高效的执行,Objective-c中引进了重用队列的机制,重影现象也是在重用队列时经常遇到的问题,那么如何解决这个问题呢?下面给出了几种解决办法. 第一种解决方法 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { NSArray *subViews = cell

UITable的重用问题

UITableViewCell重用 为了能够保证tableViewCell能够高效的执行,Objective-c中引进了重用队列的机制,重影现象也是在重用队列时经常遇到的问题,那么如何解决这个问题呢?下面给出了几种解决办法. 第一种解决方法 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { NSArray *subViews = cell

iOS关于UITabView和UIAlertController,UIAlertAction以及UINavigation,值修改的传递页面推送

关于UITabView和UIAlertController,UIAlertAction以及UINavigation,值修改的传递 集合嵌套集合的操作 声明 两个必须的的代理 实现部分代码 - (void)viewDidLoad { [super viewDidLoad]; // 创建一个TabView self.tabv = [[UITableView alloc] initWithFrame:self.view.frame style:UITableViewStyleGrouped]; sel

iOS开发-UI (八)TableView

知识点: 1.UITableView使用 2.UITableView分段功能 3.UITableViewCell重用机制 ======================= UITableView使用 1.UITableView作用 2.UITableView创建 - (id)initWithFrame:(CGRect)frame style:(UITableViewStyle)style; UITableViewStyle: UITableViewStylePlain       列表模式 UIT

IOS的一些小知识

1.停止UIView动画的方法: #import<QuartzCore/QuartzCore.h> [self.view.layer removeAllAnimations]; 2.block页面传值方法: 3.UITableViewCell中cell坐标转换 cell相对self.view的定位: CGRect rect=[self.view convertRect:cell.frame fromView:tableView]; 找到cell的相对位置后cell上button获取其他控件的相