不管是刚开始学,还是现在网上一些资料,都讲明了 UITableView
重用 cell
的原理,以及好处。但是,有的地方没有细讲,导致了我(可能也有你)对重用造成了误解。
UITableView
cell
先说下我的误解:我之前认为
cell
的重用,就是对内存进行了优化,不用创建很多个
cell
,造成内存消耗太多。
但是,最近我发现这种理解是错误的,而且是大错特错。下面先说明下结论:
cell
重用会造成更多的内存消耗。
当然了,空口无凭,事实说话。下面根据测试用例进行具体分析。测试用例可以在这里下载。
首先,测试用例包括几种情况:
- 代码创建
,代码重用UITableView
/ 代码不重用UITableViewCell
。UITableViewCell
-
创建xib
,代码重用UITableViewController
/ 代码不重用UITableViewCell
。UITableViewCell
-
创建storyboard
,UITableViewController
的UITableView
为content
/Static Cells
,代码重用Dynamic Prototypes
/UITableViewCell
重用storyboard
/ 代码不重用UITableViewCell
。UITableViewCell
然后,我在
cell
内部作了一些操作:
- 创建一个静态变量
,用于表示initCount
创建的个数。cell
- 创建一个静态变量
, 用于表示deallocCount
释放的个数。cell
- 在
方法中,清空dealloc
initCount
,以免影响下次的数据。deallocCount
最后,就看测试数据了。
先看下每种情况下
cell
创建个数,销毁个数。
- 使用代码的情况下:
- 重用时,创建 17 个,销毁 17 个。
- 不重用时,当前存在的最大个数为 17 个。以后伴随着滑动,cell 会继续创建,当然也会继续释放,维持着最大为 17 个的状态。
- 使用
xib
的情况下:
跟第一种情况一样。其实这种情况,只有
创建的方式不一样,其他一样。所以情况一样也是正常的。UITableView
- 使用
的情况下:storyboard
的UITableView
为content
时:Dynamic Prototypes
- 重用时,不管是代码注册重用,还是
设置标识重用,都是创建 16 个,销毁 16 个。storyboard
- 不重用时,当前存在的最大个数为 16 个。以后伴随着滑动,cell 会继续创建,当然也会继续释放,维持着最大为 16 个的状态。
的UITableView
为content
Static Cells
时:
跟
情况下是一样的。Dynamic Prototypes
- 重用时,不管是代码注册重用,还是
通过这些数据,基本上就知道了,即使
cell
不重用,也不会造成内存消耗太多。虽然每次都会创建新的
cell
,但是不需要的
cell
也会被销毁。而且如上所说,不重用时,最大的个数与重用情况下一样,所以不重用时的内存消耗最大情况下跟重用一样,而且,当
cell
重用的时候,必定需要额外的操作存储这些数据,所以,
cell
不重用时的内存是小于
cell
重用状态下的。
即便是这样,可能还是没有说服力,下面具体看看使用代码情况下重用与不重用的两张图。
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIlVnc01zdhJ3PH5EUuEzLcJXZ0NXYt9CXi9Gbi9CXlNXdlJ1dllmVlxmYhRFdzVGVvwVMpVHan5WYpp2Lc12bj5iY1hGdpd2Lc9CX6MHc0RHaiojIsJye.png)
注意,这里由于
cell
比较简单,所以我在
cell
初始化时,添加了一些数据,便于内存方面更加容易区分。
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
initCount++;
NSLog(@"initCount -- %d", initCount);
// 添加大数据,用于内存区分
self.array = [NSMutableArray array];
for (int i = 0; i < 100000; i++) {
[_array addObject:@(i)];
}
}
return self;
}
可以看到,重用时,内存为 24.6 M ;不重用时,内存为 24.0 M 。其他情况下,不再说明,有兴趣下载项目自己测试一下。
当然,这里只是考虑内存方面,其他方面,
cell
重用的优势还是非常有效的。比如
cpu
fps
。在重用情况下,
cpu
的使用还是比较低的,
fps
也不会掉帧的。
其实,从缓存的角度讲,缓存的存在就是使用存储换时间,提高效率的。这里
cell
的重用很明显也是一种缓存。
cell
的重用,主要还是为了使列表滑动更加顺畅,因为控件的创建消耗的时间还是比较大的。
但是,如果
cell
种类很多,而且不重复,那么使用
cell
重用就没有多大意义了。当然,如果这个页面有很多干货,很多人会滑来滑去,还是有必要使用
cell
重用的,这样,第二次开始滑动的流畅度就会明显提高了。
上面只是说了重用与不重用的区别,下面顺便说下不同情况下,重用 cell
的区别。
cell
- 不同方式创建
情况下:UITableView
-
与 代码 没什么区别xib
-
下storyboard
两种情况不影响dynamic/static
重用,cell
情况下,可以实现static
的数据源方法,也可以不实现。如果没有实现,tableview
的个数由cell
中设定的值决定;如果实现了,storyboard
个数由数据源决定的。cell
- 创建重用
的区别。cell
- 代码、
一样,xib
/initWithStyle
两种方式registerClass
-
中,stroyboard
/registerClass
中storyboard
加标识两种方式。cell
- 获取重用cell的区别。
- 代码、
一样,都是代码,如果使用xib
创建 , 必须通过initWithStyle
方法获取dequeueReusableCellWithIdentifier:
。 如果使用cell
,可以使用registerClass
或者dequeueReusableCellWithIdentifier:
两种方式获取dequeueReusableCellWithIdentifier:forIndexPath:
。cell
-
中,不管是代码注册storyboard
,还是cell
中加storyboard
标识,都要使用cell
获取dequeueReusableCellWithIdentifier:forIndexPath:
。cell
-
cell
的初始化。
当重用
并且在cell
中通过添加标识重用storyboard
时,cell
创建时会调用cell
方法,其他情况下会调用awakeFromNib
方法。注意,这里的initWithStyle:reuseIdentifier:
不存在对应的cell
文件。xib
以上,就是最近对
UITableView
重用的新理解。其他没有涉及的情况,有兴趣可以自己动手试试,也可以向我的测试用例提交代码。