UITableView重用机制介绍
重用机制是UITableView保持高性能的最基本的手段,初级开发者一般都会用到。在cellForRowAtIndexPath方法中,通过dequeueReusableCellWithIdentifier获取cell,如果获取到就直接更新数据或者UI,取不到的时候就通过alloc创建cell,并设置重用标识reuseIdentifier。
- (nonnull UITableViewCell *)tableView:(nonnull UITableView *)tableView cellForRowAtIndexPath:(nonnull NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"identifier"];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"identifier"];
//重用机制,虽然有100条数据,但只创建了22个cell
NSLog(@"创建了cell---%@", self.datasource[indexPath.row]);
}
cell.textLabel.text = self.datasource[indexPath.row];
return cell;
}
在上面的数据里,我创建了100条数据,但cell只创建了22次,比一屏数据多3条左右。
手动实现重用机制
我们通过一个辅助类ReusePool来实现自己的重用。
@interface ReusePool()
@property (nonatomic, strong)NSMutableSet *useSet;
@property (nonatomic, strong)NSMutableSet *waitingUseSet;
@end
@implementation ReusePool
-(instancetype)init {
self = [super init];
if (self) {
self.useSet = [[NSMutableSet alloc] init];
self.waitingUseSet = [[NSMutableSet alloc] init];
}
return self;
}
(UIView *)dequeueReuseView {
UIView *view = [self.waitingUseSet anyObject];
if (!view) {
return nil;
}
[self.waitingUseSet removeObject:view];
[self.useSet addObject:view];
return view;
}
(void)addReuseView:(UIView *)view {
[self.useSet addObject:view];
}
(void) reset {
UIView *view = nil;
while ((view = [self.useSet anyObject])) {
[self.useSet removeObject:view];
[self.waitingUseSet addObject:view];
}
}
@end
@end
该类有2个Set。useSet 存放没有使用但已经放入到重用池中的view。waitingUseSet 存放等待重用的view。看一下使用方法。
- (void)addViews {
[self reloadViews];
self.clickedCount++;
int max = 5;
if (self.clickedCount / 2) {
max = 10;
}
for (int i=0; i<max; i++) {
UILabel *label = (UILabel *)[self.pool dequeueReuseView];
if (!label) {
label = [[UILabel alloc] init];
label.font = [UIFont systemFontOfSize:18];
label.textColor = [UIColor blackColor];
label.textAlignment = NSTextAlignmentCenter;
[self.pool addReuseView:label];
NSLog(@"创建了");
} else {
NSLog(@"重用了");
}
label.text = @(i).stringValue;
label.frame = CGRectMake(0, i * 44 + 128, self.view.bounds.size.width, 44);
[self.view addSubview:label];
}
}
(void) reloadViews {
for (UIView *view in self.view.subviews) {
if ([view isKindOfClass:[UILabel class]]) {
[view removeFromSuperview];
}
}
[self.pool reset];
}
(void) reloadViews { for (UIView *view in self.view.subviews) { if ([view isKindOfClass:[UILabel class]]) { [view removeFromSuperview]; } } [self.pool reset]; }
每次更新视图的时候都会调用addViews方法,根据按钮的点击次数决定本次渲染的视图个数。在每次刷新之前,我们需要将pool重置reset 将useSet中的数据放置到watingUseSet中。第一次刷新的时候创建5个label,重用池中没有数据,刷新结束之后,useSet中有5个视图,第二次刷新的时候先将useSet中的5个视图放到waitingUseSet中,在创建label的时候先从waitingUseSet中取,有数据就返回视图,并将视图重新加入到useSet中,没有数据就返回nil,所以第二次刷新完之后,重用了5个视图,但useSet中增加到了10个可重用视图,之后每次刷新数据都是10个视图重用,不会再次创建。
源代码
关注面试小集,回复iOS获取相关源码。
总结
通过重用机制,我们可以提高应用的流畅性,重用机制的关键在于空间换时间,减少了视图重新创建所花费的时间。