UITableView性能提升和优化(第3章)之三

翻译继续进行。。。

创新互联长期为上千多家客户提供的网站建设服务,团队从业经验10年,关注不同地域、不同群体,并针对不同对象提供差异化的产品和服务;打造开放共赢平台,与合作伙伴共同营造健康的互联网生态环境。为榆树企业提供专业的成都网站设计、做网站、成都外贸网站建设公司榆树网站改版等技术服务。拥有十多年丰富建站经验和众多成功案例,为您定制开发。

图3-6中的UITableViewCell有4张图片,还有一个不同颜色的subview。subview是开发者通常使用的一种方式,如果你想要一个不同的背景色或使内部的view管理起来更加简单的话。这种方法会导致table view滚动时的性能问题,所以你应该尽量避免使用它。

现在,让我们来看一下使用新方法的源代码,我自己绘制view,不再使用subview。你会看到用这种方法实现时需要做的工作,然后我会总结不同技术的优点和缺点。示例源代码来自DrawingCellViewController这个工程。下面是它的主要源代码。

For UITableViewController:

- (UITableViewCell *)tableView:(UITableView *)tableViewcellForRowAtIndexPath:(NSIndexPath *)indexPath {

   static NSString *CellIdentifier = @"CellIdentifier";

   CustomDrawingTableViewCell *cell = (CustomDrawingTableViewCell *)       [self.tableViewdequeueReusableCellWithIdentifier:CellIdentifier];

   if (cell == nil) {
   cell = [[CustomDrawingTableViewCell alloc]

initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];}

   [cell updateMyCell];

  return cell;

}

正如你所看到的,UITableViewController的主要代码并没有变化。这个和标准的UITableViewCell的不同之处在于你是如何初始化你的cell。例如,

[[CustomDrawingTableViewCell alloc] initWithStyle:UITableViewCellStyleDefaultreuseIdentifier:CellIdentifier];

相比

[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];

在自定义的UITableViewCell(例如,CustomDrawingTableViewCell)中

(id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString

*)reuseIdentifier {
   if (self = [super initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseIdentifier]) {

           CGRect subFrame = CGRectMake(0.0, 0.0,

           self.contentView.bounds.size.width, self.contentView.bounds.size.height);

           drawingView = [[CustomDrawingView alloc] initWithFrame: subFrame];

           drawingView.autoresizingMask = UIViewAutoresizingFlexibleWidth |UIViewAutoresizingFlexibleHeight;

           [self.contentView addSubview:drawingView];

       }

       return self;

}

现在到了最重要的部分:如何绘制文本,图片和在view中进行控制。

CustomDrawingView.m


- (void)drawRect:(CGRect)rect {

       self.backgroundColor = [UIColor whiteColor];
       // Drawing code.
       [self.userName drawInRect:CGRectMake(70,0, 95, 21) withFont:userNameFont

lineBreakMode:UILineBreakModeTailTruncationalignment:UIBaselineAdjustmentAlignBaselines];

       // Drawing Image
       [self.avatarImage drawInRect:CGRectMake(20, 5, 36, 34)];

       // Drawing button

       [self.button drawInRect:CGRectMake(50, 5, 36, 34)];

}

简而言之,在UITableViewController构造一个自定义的UITableViewCell和之前是一样的;你仅仅需要在在dequeue的时候判断cell是否为nil,如果为nil就初始化一个新的对象。在初始化方法的内部,你必须添加一个subview到cell的内容中。对于subview,你需要复写drawRect方法,然后drawInRect方法绘制文本或图片。

自己绘制view的代码之所以比从nib文件加载或用创建添加subview的方法快,最直接的原因是GPU(图形处理单元)运行了绘制代码。GPU在渲染和显示UI是非常快的;因此,绘制代码是处理复杂subviews的

最快的方法。

注意:非常重要的是设置CustomDrawingView的背景为白色。默认是黑色的。

从这些例子中你能学到什么?

从上面的两个例子中,你应该记住一些基本的知识。

  • 使用ReuseIdentifier。它能帮助你提升性能。

  • 尝试减少cell预加载过程中的工作,尤其是从文件IO或网络IO加载图片的时间和效率。这样可以在最短的时间内显示图片。

  • 如果你的应用有很多的subviews或复杂的结构,考虑自己用代码来绘制。这样可以让GPU来加速整个过程。

警告:从上面的测试结果可以看出,fps的结果变得越来越好,几乎接近最理想的值60。但是,使用这种方法,你不能享有InterfaceBuilder构建UI的好处。你总是要自己计算位置和大小,然后把这些信息放在drawRect方法中。这样很快会导致维护和功能膨胀(在应用中添加过多的功能)的问题。因此,谨慎使用drawRect,避免过多优化。

其他技术

我已经讨论了table view 滚动时提升性能的重要技术。还有其他一些小技术你通常是不需要使用的,但是在这里我也会介绍。如果你能理解这些概念,你可以把这些技术应用到其他的情况下。

缓存高度

无论是否需要创建一个新的cell,你需要缓存rows的高度,因为这是TableView所需要的信息。如果你的cell的高度是固定的,你不必担心。然而,如果它不是固定的,你需要确保你的cell计算足够的快。

可以尝试使用如下代码:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath*)indexPath {

   return 80;

}

避免使用下面的代码:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath*)indexPath {

for (int i = 0; i < 100; i++) {

// find the smallest possible height for the row

}

   return smallestHeight;

}

当需要渲染cell或者在动画过程中需要编辑/重排序cell的时候,OS会多次运行第一段代码段。xiang像第二段代码中有一个循环,无论是否需要知道cell的高度,OS都会运行一个重复100次的循环。

透明度

如果有可能,使UITableView所有的layers和subviews保持不透明。当一个view是透明的,iOS需要渲染一个像素两次或多次,这是因为一个像素同时属于很多subviews。这是一个非常耗时的过程。

通过代码或者InterfaceBuilder能够很简单的做到。开发者应该多次检查所有的subviews是不透明的。图3-7显示了如何在cell中设置subview为不透明的checkbox。

UITableView性能提升和优化(第3章) 之三

对于自定义代码,你可以通过代码来设置,如下:

view.opaque = YES;

避免使用图形特效

避免在UIImage中使用复杂的图形特效(例如渐变)。你应该对CoreAnimation进行一些小的配置,然后使用它来检查图形特效,如图3-8和3-9。

UITableView性能提升和优化(第3章) 之三

编辑/重排序 性能

在前面的部分,我展示了直接绘制的方法,你能够显著的优化apps的性能。但是绘制的这种方法,在动画和重排序性能中,会有很多严重的问题。

当你使用subviews的时候,动画会变的很快,在动画过程中UIKit不会重绘或修改任何东西。因此,对于UIKit来说,使用subview比自己通过代码来绘制更快。如果在动画或重排序中,你通过代码自己绘制view,你必须再一次绘制然后重新填充到新view中。这样导致做了很多工作,包括代码的创建和维护。

当你在用UITableViewController遇到性能问题时,你必须进行一个权衡。我的建议是:如果你能确保不会有很多的subviews,或者你允许用户对cell进行编辑或重排序,那么就用subview这种方法。虽然可能会使得app运行慢一些,但也已经不错了。

总结

通过这里例子的源代码分析,你已经学会了很多提升性能的重要技术了。

  • 使用NSLog和CoreAnimation进行仔细的测试:通过一个实际的例子,我让你看到了如何使用Instrument和benchmark工具有效的理解问题的实质,以及在每一步优化之后提升了多少性能。

  • 恰当的重用cell:这是第一步,也是最重要的一步。重用一个cell非常简单,但是很多应用都会漏了这一步。因此,你如果有很多性能问题,确保多次检查这方面的问题。

  • 恰当的 缓存/重用 图片/数据:另外一个重要的步骤就是当返回一个cell显示的时候,减少数据加载和逻辑处理的时间。

  • 减少总的加载和计算时间:并不是仅仅只有IO会减慢和阻塞UI线程;任何数据的处理都会减慢这个过程。因此,你应该总是尽可能的减少这个过程的处理时间。

  • 自行绘制cell:在渲染table view的时候,为了充分利用GPU计算密集型的优势,你应该考虑使用直接绘制的方法。这样会显著的提升渲染的速度,增加测量的性能;fps几乎接近最大。你可以通过复写drawRect方法绘制自己的cell,然后在每个元素中调用不同的方法绘制每个UI元素。

  • 透明度:当开发者将他们的UI元素放进view的时候,这是经常会遇到的小问题。如果他们没有设置view为不透明,渲染的时候就要对同一个点绘制两次或多次。

  • 缓存高度:这是开发者经常会犯的另一个小错误。每当需要一个新的cell,每次都会有两个主要的方法被调用。

  • 避免图形特效:在cell中有越多的特效,渲染的过程就越慢。因此,你应该对此进行测试。你可以使用CoreAnimation来查看每个UI元素渲染的效率。

  • 编辑/重排序性能:滚动性能优化,对于编辑或重排序来说,可能会带来一些问题,因为UIKit和动画框架已经多subview进行了优化。如果你是自己绘制的话,这些框架的优化将不起作用。


文章标题:UITableView性能提升和优化(第3章)之三
网站网址:http://ybzwz.com/article/ggjigg.html