『iOS性能优化』--UITableView

源码 2024-9-12 22:44:03 60 0 来自 中国
优化方案

一、善用重用标识
二、设置预估行高,预先缓存动态行高
三、减少SubViews层级、异步绘制、制止离屏渲染、使用Hidden隐蔽图层
四、分屏加载数据,预先异步哀求数据
五、滑动TableView时,按需加载内容
六、cell类中应该制止哀求网络加载数据
七、在willDisplayCell:forRowAtIndexPath:署理方法中的绑定命据
一、善用重用标识

这个属于根本知识范畴,就不再过分的讲授了。
只须要相识使用static修饰重用标识名称可以或许保证这个标识只会创建一次,进步性能。接着就是调用dequeueReusableCellWithIdentifier:方法获取缓存池中的Cell。假如没有就调用initWithStyle:ReusIdentifier:方法创建一个新的Cell。留意事先须要调用registerNib/registerClass方法为TableView注册一下标识。
二、设置预估行高,预先缓存动态行高

1. 设置预估行高

我们知道UITableView是通过UITableView署理方法heightForRowAtIndexPath:方法来设置行高。自从iOS8.0之后,苹果新增了self-sizing cell的概念,也是cell可以本身盘算行高,使用须要满意是三个条件:
(1) 使用Autolayout举行UI布局约束
(2) 指定TableView的estimatedRowHeight属性的默认值
(3) 指定TableView的rowHeight的属性为UITableViewAutomaticDimension。
TableView在加载数据时会先通过estimatedRowHeight:AtIndexPath处理处罚全部数据,此时我们只须要提供一个大略的高度,待到cell对象创建之后再去设置cell的真实高度。而且只会处理处罚当前屏幕范围内的cell,如许子会明显的提升加载的性能。
- (CGFloat)tableViewUITableView *)tableView heightForRowAtIndexPathNSIndexPath *)indexPath {      return 50.0;  }  - (CGFloat)tableViewUITableView *)tableView estimatedHeightForRowAtIndexPathNSIndexPath *)indexPath {      return 30.0;  }  2.预先盘算并缓存行高

自从iOS8.0之后,TableView的数据源的调解时序也发生了变化,下图左边为iOS7.0以及之前的时序,右边为iOS8.0以及之后的时序:

1.png
从上图可以很轻易的分析出,iOS8.0之后在获取cell对象之后会再次调用heightForRowAtIndexPath:方法获取行高,这也就意味着我们实在可以先创建cell对象,之后再提供行高。详细方法我们可以在cell类中添加layoutAttribute属性,纪录相应的UIEdgeInsets,然后在设置cell真实高度的时间返回。iOS7.0之前则必须在cell对象处啊给你讲爱你之前先得到全部cell的高度。
三、减少SubViews层级、异步绘制、制止离屏渲染、使用Hidden隐蔽图层

1. 减少图层层级数目

当我们自界说某个cell,并在cell上添加大量的体系控件后,在创建该cell对象时体系会调用底层接口举行绘制,大量的添加利用会斲丧很大的资源同时会影响渲染的性能。
2. 异步绘制

办理因图层层级多造成的性能题目,我们可以通过drawRect:方法,调用Core Graphics框架中的API举行异步绘制,进步服从。drawRect:本身是移步的。别的drawRect:中大量的绘制也会造成内存的增长,可以使用CAShapeLayer来取代。
3. 减少多于的绘制利用

在实现drawRect:方法的时间,他的rect参数就是我们须要绘制的地域,在rect范围之外的地域不要绘制,否则会斲丧相称大的资源。
4. 图片加载时机选择

起首在cell中添加图片应该只管制止使用imageWithNamed:方法,由于该方法会将图片缓存到内存中。而且应该使用imageWithContentsOfFile:方法来更换,该方法在图片使用完后体系会自动开释资源,并不会缓存下来。别的联合SDWebImage框架的使用可以明显进步图片加载的性能。
5. 制止动态添加图层

在cell中应该只管制止动态创建图层。在初始化cell的时间一并将全部的图层预先创建好,通过hidden属性控制子图层的表现或隐蔽,由于单纯的表现利用要比创建快得多。
6. 制止离屏渲染

什么是离屏渲染?我们知道iOS底层的渲染框架使用的是OpenGL ES。OpenGL中,GPU渲染屏幕方式有两种:当前屏幕渲染(On-Screen Rendering)和离屏渲染(Off-Screen Rendering)。它们的区别是当前屏幕渲染利用是在当前屏幕缓冲区完成,而离屏渲染会在别的一个新开发的缓冲区完成渲染利用,开启离屏渲染的代价就是须要新开发一块新的缓冲区,在渲染的过程中还会多次切换上下文,这些都是很斲丧性能的。

  • 为图层设置遮罩(layer.mask)
  • 设置图层的layer.masksToBounds/view.clipsToBounds属性为True
  • 设置图层的layer.allowsGroupOpacity的属性为True和layer.opacity小于1.0
  • 设置图层阴影(layer.shadow)
  • 设置图层的layer.shouldRasterize的属性为True
  • 具有layer.cornerRadius,layer.edgeAntialiasingMask,layer.allowsAntialiasing的图层
  • 文本(任何种类,包罗UILabel、CATextLayer、Core Text等)
  • 使用CGContext在drawRect:方法中绘制
    上述情况均会造成离屏渲染。
7. 图片圆角优化

使用贝塞尔曲线 + Core Graphics框架设置圆角
- (void)setImageCircularEdgeUIImageView *)imageView {      //开始对imageView举行画图      UIGraphicsBeginImageContextWithOptions(imageView.bounds.size, NO, 1.0);      //使用贝塞尔曲线画出一个圆形图      [[UIBezierPath bezierPathWithRoundedRect:imageView.bounds cornerRadius:imageView.frame.size.width] addClip];      [imageView drawRect:imageView.bounds];      imageView.image = UIGraphicsGetImageFromCurrentImageContext();      //竣事画图      UIGraphicsEndImageContext();  }  使用贝塞尔曲线 + CAShapeLayer设置圆角
- (void)setImageCircularEdge2UIImageView *)imageView {      UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:imageView.bounds byRoundingCorners:UIRectCornerAllCorners cornerRadii:imageView.bounds.size];      CAShapeLayer *maskLayer=[[CAShapeLayer alloc] init];      //设置巨细      maskLayer.frame = imageView.bounds;      //设置图形样子      maskLayer.path = maskPath.CGPath;      imageView.layer.mask = maskLayer;  }  8. 图片阴影优化

- (void)setImageShadowUIImageView *)imageView {      imageView.layer.shadowColor = [UIColor grayColor].CGColor;      imageView.layer.shadowOpacity = 1.0;      imageView.layer.shadowRadius = 2.0;      UIBezierPath *path=[UIBezierPath bezierPathWithRect:imageView.frame];      imageView.layer.shadowPath = path.CGPath;  }  四、分屏加载数据,预先异步哀求数据

在我们的项目开发中列表视图的应用许多,偶然数据比力多的时间我们不大概一次加载全部数据,如许子会导致内存的暴涨,同时用户不愿定会欣赏全部的信息,造成资源浪费。这时我们可以通太过屏加载来办理这个题目,比如第一次加载10条数据,当我向上滑动列表的时间通常我们会再次去哀求数据接口获取下一个10条数据。这个时间假如我们不做任何的处理处罚,那么我会发现每次划过10条数据的时间列表都须要停顿一下,等待数据加载。如许子我们的列表就体现的不是很流畅了,那么怎么办理这个题目呢?
提前异步预加载数据!第一次加载完10条数据之后可以再预先加载下10条数据,当划过第10条数据时,再哀求下10条数据。如许子我们的列表就体现的很流畅了。
五、滑动TableView时,按需加载内容

有些情况下我们大概会去快速的滑动列表,这时间实在会有大量的cell对象被创建、被重用,实在我们大概只是去欣赏列表克制的那一页的上下肯定范围内的信息,前面快速划过的那些信息对我们来说都是无用的。有什么方法让我们只去加载最后那页的目标范围内的列表数据呢?那就是通过ScrollView的署理方法'scrollViewWillEndDragging:withVelocity:targetContentoffset:'来实现的。
#pragma mark - UIScrollViewDelegate  //按需加载 - 假如目标行与当前行相差凌驾指定行数,只在目标滚动范围的前后指定3行加载。  - (void)scrollViewWillEndDraggingUIScrollView *)scrollView withVelocityCGPoint)velocity targetContentOffsetinout CGPoint *)targetContentOffset {      NSIndexPath *targetPath = [_myTableView indexPathForRowAtPoint:CGPointMake(0, targetContentOffset->y)];      NSIndexPath *firstVisiblePath = [[_myTableView indexPathsForVisibleRows] firstObject];      NSInteger skipCount = 8;      if (labs(firstVisiblePath.row - targetPath.row)>  skipCount) {          NSArray *temp = [_myTableView indexPathsForRowsInRect:CGRectMake(0, targetContentOffset->y, _myTableView.frame.size.width, _myTableView.frame.size.height)];          NSMutableArray *arr = [NSMutableArray arrayWithArray:temp];          if (velocity.y<0) {              NSIndexPath *indexPath = [temp lastObject];              if (indexPath.row+33) {                  [arr addObject:[NSIndexPath indexPathForRow:indexPath.row-3 inSection:0]];                  [arr addObject:[NSIndexPath indexPathForRow:indexPath.row-2 inSection:0]];                  [arr addObject:[NSIndexPath indexPathForRow:indexPath.row-1 inSection:0]];              }          }          [_dataList addObjectsFromArray:arr];      }  }  targetContentOffset是TableView减速到克制的地方,velocity表现速率向量。
六、cell类中应该制止哀求网络加载数据

假如确实有需求不可制止,可以将网络加载使命添加到Runloop中,设置DefaultRunloopModule模式。如许子可以起到延伸加载的作用。
七、在willDisplayCell:forRowAtIndexPath:署理方法中的绑定命据

初学iOS的时间,各类教程以及册本中都喜欢在cellForRowAtIndexPath:方法中绑定命据,然后此时的cell实在还未表现,该方法中包罗了大量的布局、绘制干系的利用。我们应该在该方法中只管简化我们自身的逻辑利用。这时我们可以使用在willDisplayCell:forRowAtIndePath:方法中绑定命据。
#pragma mark - UITableViewDataSource  - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {      static NSString *cellIdentifier = @"MyTableViewCell";      UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];      if (!cell) {          cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];      }      return cell;  }  #pragma mark - UITableViewDelegate  - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {      NSDictionary *dict = self.dataList[indexPath];      [cell updateData:dict];  }
您需要登录后才可以回帖 登录 | 立即注册

Powered by CangBaoKu v1.0 小黑屋藏宝库It社区( 冀ICP备14008649号 )

GMT+8, 2024-10-18 20:20, Processed in 0.163408 second(s), 35 queries.© 2003-2025 cbk Team.

快速回复 返回顶部 返回列表