一、 UITableView 的重用机制:

  iphone重用机制是苹果为了实现大量数据显示而采用的一种节省内存的机制,比如在UITableView和ScrollView 等地方。为什么要“可重用”???对于我们的项目来说,内存控制是必不可少的,如果一个tableview有几百个cell,这个内存消耗是很大的,而且有些cell里面都有image之类的很占内存的资源存在的话,那这样很容易出现memory warning甚至crash掉,这不是我们想要看到的。对此,tableview实现了它自己的管理方法dequeueReusableCellWithIdentifier。ps:我们在某些项目中scrollview来显示很多张image,在scrollview滑动中也要这样处理,来避免内存的过度消耗,只不过tableview它已经实现了这个方法,而不用我们自己去写。

 

二、 使用 UITableView 会出现 Cell 数据错误的问题(这样的情况多在使用 StoryBoard 时出现)

  苹果文档中不鼓励我们在UITableViewCell中添加subView,最好采用自定义Cell,将需要的SubView添加到Cell当中。使用addSubView在每项上添加视图的时候会有重叠的现象。例如,UITableView中的Cell ,如果在cell上添加子视图,则在使用苹果的重用机制的时候,会重现子试图重叠的现象。或出现开头提到的两个问题。如果在数据量不是很多的时候,可以手动屏蔽掉UITableView的重用机制。

 

三、UITableView的重用机制的实现关键

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

首先,我们要清楚这一点,这个函数是做什么的,它的文档说明如下:
returns a reusable table-view cell object located by its identifier。它返回的是一个受identifier管理定位的可重用的tableViewCell,这里重点就在于“可重用”这3个字上。

我们来看它的实现方法,举个例子来说,在系统刚启动时,tableview可以显示多少个cell,在这里我们假定为10个,在刚开始的时候tableview会生成10个tableviewcell,并且对应有自己的tag值,假定为0-9。(ps:苹果官方的视频中也提到了,尽量避免频繁的add/remove view或者控件之类等。自定义啊自定义,相对于Android 空间的自定义,)所以采用下面的方法来实现:在tableview向上滚动的时候,tag为0的cell将不再显示;然后我们把tag为0的cell移动到tag为9的cell下面,重新设置相关的属性,然后将tag为1的cell移动到tag为0的cell下面……依此类推。这也就是所谓的“可重用”。

但是此时被移动的tag为0的cell的一些属性还是保持不变的(包括之前添加的subView),因此就会出现一些无厘头的bug(看了这么多,到这里是不是松了口气? )。

 

四、如何解决 UITableView 的重用机制所带来的错误 

方案一:首推使用 Xib 来代替故事版(StoryBoard)的自定义 UITableViewCell

关键代码:



A* cell = (A*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
 if (cell == nil) {
     NSArray* nib = [[NSBundle mainBundle] loadNibNamed:@"VideoCell" owner:self options:nil];
    cell = [nib objectAtIndex:0];
}



ps:注意这里只能使用 dequeueReusableCellWithIdentifier: 方法而不能使用 dequeueReusableCellWithIdentifier:  forIndexPath: 方法,后者似乎是属于故事版专用

方案二:移除所有 UItableViewCell 添加的 subView



UITableViewCell *cell =[tableView dequeueReusableCellWithIdentifier:CellIdentifier];

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

NSArray*subviews = [[NSArray alloc]initWithArray:cell.contentView.subviews];
for (UIView *subview in subviews) {
    [subview removeFromSuperview];
}