UITableView 重用机制

UITableView 最核心的部分就是 UITableViewCell 的重用机制(初学者必问面试题.通俗的说: UITableView 有一个 Cell 对象的重用池,其中存放着当前页面显示的 Cell(在某些设备上,可能会再多几个.当 UITableView 滚动时,离开屏幕的 Cell 会被放到重用池中.有新的 Cell 要显示,则又从重用池中取.这样的好处显而易见,如果 UITableView 有一万行,我们不需要真的去创建一万个 Cell 对象.

缓存高度

如果我们的 UITableViewCell 有复杂的动态高度,那么我们需要缓存每个 Cell 的高度.因为 UITableView 是继承 UIScrollView,需要先确定 contentSize.既然我们的 Cell 需要动态计算高度的,如果我们有一万个 Cell, 那么在创建 UITableView 时 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;会被调用一万次哦.如果你 reload 一下,又是一万次哦~.当 UITableView 滚动时,又会调用 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;既然这个方法会被调用那么多次,我们可以在网络到 UITableView 的数据源时,计算出一个 Cell 高度数组.

透明图层

透明图层对渲染性能会有一定的影响,因为混合(blending)是渲染中最慢的操作,系统会将透明图层与下面的视图混合起来计算并绘制图层属性,减少透明图层并使用不透明的图层来替代它们,在不透明的视图里标明 opaque 属性以避免无用的 Alpha 通道合成,可以大量提高 GPU 的计算速度.
可以通过 instrument 的 Core Animation 中开启 Color Blended Layers
,然后红色的部分就是我们需要重点消灭的区域.

减少离屏渲染

CALayer 的 border、圆角、阴影、遮罩(mask),CAShapeLayer 的矢量图形显示,通常会触发离屏渲染(offscreen rendering).与之相对的是当前屏幕渲染(On-Screen Rendering),指的是渲染操作是用于在当前屏幕显示的缓冲区进行.离屏渲染的概念来自于 OpenGL 中 GPU 渲染屏幕的两种方式: On-Screen Rendering(当前屏幕渲染)Off-Screen Rendering(离屏渲染)。 指的是:在当前屏幕以外新开辟一个缓冲区进行渲染操作。离屏渲染造成卡顿的原因是:离屏渲染需要多次切换上下文环境,先是从当前屏幕(On-Screen)切换到离屏(Off-Screen),等到离屏渲染结束以后,将离屏缓冲区的渲染结果显示到屏幕上又需要将上下文环境从离屏切换到当前屏幕,而上下文环境的切换是一项高开销的动作。
通常我们会一个 view 设置阴影会使用 shadowoffset

1234
UIView *diamondView = [[UIView alloc] init];diamondView.layer.shadowOffset = CGSizeMake(1.0f, 1.0f);diamondView.layer.shadowRadius = 5.0f;diamondView.layer.shadowOpacity = 0.5;

但是这种方式会触发离屏渲染造成不必要的开销,那么既要实现阴影图层,又要减少离屏渲染,提高性能的话.有什么更好的方式么?

123
UIView *diamondView = [[UIView alloc] init];diamondView.layer.shadowPath = [UIBezierPath bezierPathWithRect:CGRectMake(diamondView.bounds.origin.x + 1, diamondView.bounds.origin.y + 1, diamondView.bounds.size.width, diamondView.bounds.size.height)].CGPath;imageView.layer.shadowOpacity = 0.5;

但是 shadowPath 只适用于给规则的矩形生成阴影路径.如果我们迫不得已要使用 shadowoffset,可以尝试开启 CALayer.shouldRasterize 属性, 图像将会被缓存起来并绘制到实际图层的 contents 和子图层.将原本在 GPU 中的一些工作让 CPU 来做,让两者达到一个平衡.但是这并不是有一个全优解,因为光栅化原始图像需要时间,而且会消耗额外的内存.所以一定要避免在内容不断变动的图层上使用,不然缓存的优势将荡然无存.
最完美的解决方案是使用 Core Graphics 绘制圆角 UIImage 设置给 UIImageView 然后插入到 UIView 中去。

按需加载 Cell

当快速滑动 UITableView 的时候,计算出将要滚动到的 Cell 开始预加载,在滚动过程中的 Cell 选择不加载,这样可以极大的提高流畅度,同时也会出现一个问题就是,过程中会出现大量白色的 Cell.简单实现了下:
先监听 ScrollView 的滚动事件 

 tip

  1. 减少 Cell 中的 Subview 的数量
  2. 尽可能使用静态 cell
  3. 使用rowHeightsectionFooterHeight 和 sectionHeaderHeight来设定固定的高度
  4. 减少 Cell 的种类,过多的 CellIndentifier 使 Cell 的重用池中存在大量的 Cell 实例对象.