UITableView的复用机制及优化

UITableView的复用机制:

复用机制大体是这样:UITableView首先加载一屏幕(假设UITableView的大小是整个屏幕的大小)所需要的UITableViewCell,具体个数要根据每个cell的高度而定,总之肯定要铺满整个屏幕,更准确说当前加载的cell的高度要大于屏幕高度。然后你往上滑动,想要查看更多的内容,那么肯定需要一个新的cell放在已经存在内容的下边。这时候先不去生成,而是先去UITableView自己的一个资源池里去获取。这个资源池里放了已经生成的而且能用的cell。如果资源池是空的话才会主动生成一个新的cell。那么这个资源池里的cell又来自哪里呢?当你滑动时视图是,位于最顶部的cell会相应的往上滑动,直到它彻底消失在屏幕上,消失的cell去了哪里呢?你肯定想到了,是的,它被UITableView放到资源池里了。其他cell也是这样,只要一滑出屏幕就放入资源池。这样,有进有出,总共需要大约一屏幕多一两个的cell就够了。相对于1000来说节省的资源就是指数级啊,完美解决了性能问题。

复用机制是一个很好的机制,但是不正确的使用却会给我们的程序带来很多问题。下面拿 UITableView 复用 Cell 来举例:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *CellIdentifier =  @"UITableViewCell"; 
    UITableViewCell *cell = nil;
    cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    if (!cell) { 
         cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier]; 
// 偶数行 Cell 的 textLabel 的文字颜色为红色。
        if (indexPath.row % 2 == 0) { 
               [cell.textLabel setTextColor:[UIColor redColor]]; 
        }  
   }

   cell.textLabel.text = @"Title";
 // 偶数行 Cell 的 detailTextLabel 显示 Detail 文字。
      if (indexPath.row % 2 == 0) { 
       cell.detailTextLabel.text = @"Detail"; 
     }

    return cell;
}

我们本来是希望只有偶数行的 textLabel 的文字颜色为红色,并且显示 Detail 文字,但是当你滑动 TableView 的时候发现不对了,有些奇数行的 textLabel 的文字颜色为红色,而且还显示了 Detail 文字,很奇怪。其实造成这个问题的原因就是「复用」,当一个 Cell 被拿来复用时,它所有被设置的属性(包括样式和内容)都会被拿来复用,如果刚好某一个的 Cell 你没有显式地设置它的属性,那么它这些属性就直接复用别的 Cell 的了。就如上面的代码中,我们并没有显式地设置奇数行的 CelltextLabel 的文字颜色以及 detailTextLabel 的文字,那么它就有可能复用别的 Cell 的这些属性了。此外,还有个问题,对偶数行 CelltextLabel 的文字颜色的设置放在了初始一个 Cell 的 if 代码块里,这样在复用的时候,逻辑走不到这里去,那么也会出现复用问题。所以,上面的代码需要改成这样:


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 
    static NSString *CellIdentifier = @"UITableViewCell"; 
   UITableViewCell *cell = nil; 
   cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 
   if (!cell) { 
       cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
   } 

   cell.textLabel.text = @"Title";

   if (indexPath.row % 2 == 0) {
       [cell.textLabel setTextColor:[UIColor redColor]]; 
       cell.detailTextLabel.text = @"Detail";
   } else { 
       [cell.textLabel setTextColor:[UIColor blackColor]];
       cell.detailTextLabel.text = nil; 
   }

   return cell;
}

总之在复用的时候需要记住:

设置 Cell 的存在差异性的那些属性(包括样式和内容)时,有了 if 最好就要有 else,要显式的覆盖所有可能性。
设置 Cell 的存在差异性的那些属性时,代码要放在初始化代码块的外部。

上面的代码中,我们展示了- [UITableView dequeueReusableCellWithIdentifier:];
的用法。下面看看另几个 API 的用法:

@property (weak, nonatomic) IBOutlet UITableView *myTableView;

- (void)viewDidLoad { 
    [super viewDidLoad]; 
        // Setup table view. 
    self.myTableView.delegate = self;
    self.myTableView.dataSource = self;
   [self.myTableView registerClass:[MyTableViewCell class] forCellReuseIdentifier:@"MyTableViewCell"];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
     static NSString *CellIdentifier = @"MyTableViewCell"; 
     UITableViewCell *cell = nil; 
     cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
     cell.textLabel.text = @"Title";

    if (indexPath.row % 2 == 0) { 
           [cell.textLabel setTextColor:[UIColor redColor]];
    } else {
          [cell.textLabel setTextColor:[UIColor blackColor]];
    } 

    return cell;
}

我们可以看到:

- [UITableView dequeueReusableCellWithIdentifier:forIndexPath:];
必须搭配- [UITableView registerClass:forCellReuseIdentifier:];或者- [UITableView registerNib:forCellReuseIdentifier:];
使用。当有可重用的 Cell时,前者直接拿来复用,并调用- [UITableViewCell prepareForReuse]
方法;当没有时,前者会调用Identifier对应的那个注册的 UITableViewCell 类的- [UITableViewCell initWithStyle:reuseIdentifier:]方法来初始化一个,这里省去了你自己初始化的步骤。当你自定义了一个 UITableViewCell的子类时,你可以这样来用。

UITableView的优化:

优化 UITableView:

UITableView 是我们最常用来展示数据的控件之一,并且通常需要 UITableView 在承载较多内容的同时保证交互的流畅性,对 UITableView 的性能优化是我们开发应用程序必备的技巧之一。
UITableView的复用机制一节,已经提到了 UITableView 的复用机制。现在就来看看 UITableView 在复用时最主要的两个回调方法:- [UITableView tableView:cellForRowAtIndexPath:]- [UITableView tableView:heightForRowAtIndexPath:]UITableView 是继承自 UIScrollView,所以在渲染的过程中它会先确定它的 contentSize 及每个 Cell 的位置,然后才会把复用的 Cell 放置到对应的位置。比如现在一共有 50 个 Cell,当前屏幕上显示 5 个。那么在第一次创建或 reloadData 的时候, UITableView 会先调用 50 次- [UITableView tableView:heightForRowAtIndexPath:]
确定 contentSize 及每个 Cell 的位置,然后再调用 5 次 - [UITableView tableView:cellForRowAtIndexPath:]
来渲染当前屏幕的 Cell。在滑动屏幕的时候,每当一个 Cell 进入屏幕时,都需要调用一次 - [UITableView tableView:cellForRowAtIndexPath:]- [UITableView tableView:heightForRowAtIndexPath:]方法。

了解了 UITableView 的复用机制以及相关回调方法的调用次序,这里就对 UITableView 的性能优化方案做一个总结:

  • 通过正确的设置 reuseIdentifier 来重用 Cell。
  • 尽量减少不必要的透明 View
  • 尽量避免渐变效果、图片拉伸和离屏渲染。
  • 当不同的行的高度不一样时,尽量缓存它们的高度值。
  • 如果Cell 展示的内容来自网络,确保用异步加载的方式来获取数据,并且缓存服务器的 response
  • 使用 shadowPath 来设置阴影效果。
  • 尽量减少 subview 的数量,对于 subview 较多并且样式多变的 Cell,可以考虑用异步绘制或重写drawRect
    *尽量优化 - [UITableView tableView:cellForRowAtIndexPath:]
    方法中的处理逻辑,如果确实要做一些处理,可以考虑做一次,缓存结果。
  • 选择合适的数据结构来承载数据,不同的数据结构对不同操作的开销是存在差异的。
  • 对于 rowHeightsectionFooterHeightsectionHeaderHeight 尽量使用常量。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271

推荐阅读更多精彩内容