先说下我的误解:我之前认为 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
时:- 重用时,不管是代码注册重用,还是
storyboard
设置标识重用,都是创建 16 个,销毁 16 个。 - 不重用时,当前存在的最大个数为 16 个。以后伴随着滑动,cell 会继续创建,当然也会继续释放,维持着最大为 16 个的状态。
UITableView
的content
为Static Cells
时:跟
Dynamic Prototypes
情况下是一样的。 - 重用时,不管是代码注册重用,还是
通过这些数据,基本上就知道了,即使 cell
不重用,也不会造成内存消耗太多。虽然每次都会创建新的 cell
,但是不需要的 cell
也会被销毁。而且如上所说,不重用时,最大的个数与重用情况下一样,所以不重用时的内存消耗最大情况下跟重用一样,而且,当 cell
重用的时候,必定需要额外的操作存储这些数据,所以,cell
不重用时的内存是小于 cell
重用状态下的。
即便是这样,可能还是没有说服力,下面具体看看使用代码情况下重用与不重用的两张图。
注意,这里由于
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
重用的,这样,第二次开始滑动的流畅度就会明显提高了。
- 不同方式创建
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
重用的新理解。其他没有涉及的情况,有兴趣可以自己动手试试,也可以向我的测试用例提交代码。