0%

TableView 性能优化

此文整理一下 TableView 优化相关的方案和思路。

TableView 为什么会卡?

主要由以下原因:

  • cellForRowAtIndexPath: 方法中处理了过多业务
  • tableviewCell 的 subview 层级太复杂,做了大量透明处理
  • cell 的 height 动态变化时计算方式不对

优化核心思想: UITableViewCell 重用机制

简单的理解就是:UITableView 只会创建一屏幕(或一屏幕多一点)的 UITableViewCell,其他都是从中取出来重用的。每当 Cell 滑出屏幕时,就会放入到一个集合(或数组)中(这里就相当于一个重用池),当要显示某一位置的 Cell 时,会先去集合(或数组)中取,如果有,就直接拿来显示;如果没有,才会创建。这样做的好处可想而知,极大的减少了内存的开销。

Tips:

  1. 提前计算并缓存好高度(布局),因为 heightForRowAtIndexPath: 是调用最频繁的方法;
  2. 异步绘制,遇到复杂界面,参考 FacebookAsyncDisplayKitYYAsyncLayer异步绘制框架;
  3. 缓存图片( SDWebImage ),提前处理好 UIImageView 图片的尺寸按需加载而不是加载原图;
  4. 计算等耗时操作异步处理,处理完再回主线程更新 UI;
  5. 图文混排不定高度采用 CoreText 排版,缓存 Cell 高度参考YYKit
  6. 实现 Cell 的 drawRect: 方法直接绘制,减少 UIViewUIImageViewUILabel 等容器的使用。

Bonus:

  1. 正确使用 reuseIdentifier 来重用 Cell;
  2. 尽量少用或不用透明图层或 View;
  3. 如果 Cell 内现实的内容来自 web,使用异步加载,缓存请求结果;
  4. 减少 subviews 的数量在 heightForRowAtIndexPath: 中尽量不使用 cellForRowAtIndexPath: ,如果你需要用到它,只用一次然后缓存结果;
  5. 尽量少用 addView 给 Cell 动态添加 View,可以初始化时就添加,然后通过 hide 来控制是否显示;
  6. 固定高度不要实现 heightForRowAtIndexPath: 方法。

可以通过实现以下方法,可以减少高度计算次数

1
2
3
4
5
6
@property (nonatomic) CGFloat rowHeight;             // will return the default value if unset
@property (nonatomic) CGFloat sectionHeaderHeight; // will return the default value if unset
@property (nonatomic) CGFloat sectionFooterHeight; // will return the default value if unset
@property (nonatomic) CGFloat estimatedRowHeight NS_AVAILABLE_IOS(7_0); // default is 0, which means there is no estimate
@property (nonatomic) CGFloat estimatedSectionHeaderHeight NS_AVAILABLE_IOS(7_0); // default is 0, which means there is no estimate
@property (nonatomic) CGFloat estimatedSectionFooterHeight NS_AVAILABLE_IOS(7_0); // default is 0, which means there is no estimate

参考资料: