当前位置: 首页 > 工具软件 > iOS Hero > 使用案例 >

iOS UITableView性能优化 —— HERO博客

莫振
2023-12-01

1. cell重用,注册重用标识符

        tableView会根据屏幕可显示的cell个数去创建cell,当cell划出屏幕时会放到重用池,有cell需要显示时,会根据标识符先去重用池取,有则拿出来显示,没有再去创建。

 

 
  1. static NSString *identifier = @"cellIdentifier";

  2. UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];

  3. if (!cell) {

  4. cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];

  5. }

  6. return cell;

2. 避免cell的重新布局

        cell的布局填充等操作比较耗时,一般创建时就布局好,如可以将cell单独放到一个自定义类,初始化时就布局好。

 

3. 提前计算并缓存cell的属性及内容

        tableView实现数据源方法后,编译器会先调用tableView:heightForRowAtIndexPath:确定每一个cell的高度,在调用tableView:cellForRowAtIndexPath:创建要显示的cell,滚动时,每当cell进入屏幕都会计算高度,编译器知道高度后,会去创建cell,所以提前缓存cell高度可以避免重复的计算。如果cell是固定高度直接写self.tableView.rowHeight = 70;不需要实现tableView:heightForRowAtIndexPath:方法以节省不必要的开销。

 

        举个例子来说:如果当前数据源有100条数据,当前屏幕最多只能显示5个。那么刷新UITableView时,UITableView会先调用100次tableView:heightForRowAtIndexPath:方法,然后调用5次tableView:cellForRowAtIndexPath:方法;滚动屏幕时,每当Cell进入屏幕,都会调用一次tableView:heightForRowAtIndexPath:、tableView:cellForRowAtIndexPath:方法。

        所以尽量不要在tableView:heightForRowAtIndexPath:方法中调用tableView:cellForRowAtIndexPath:方法。

4. 减少cell中控件的数量       

 

         我们在cell上添加系统控件的时候,实际上系统都会调用底层的接口进行绘制,大量添加控件时,会消耗很大的资源并且也会影响渲染的性能。当使用默认的UITableViewCell并且在它的ContentView上面添加控件时会相当消耗性能。所以目前最佳的方法还是继承UITableViewCell,并重写drawRect方法。尽量使cell的布局大致相同,不同风格的cell可以使用不同的重用标识符。

 

5. 尽量不要用透明属性

        cell的opaque值设为Yes,尽量不要使用控件透明属性,cell自身也是,透明度也避免设置为0,渲染耗时比较长。

 

6. 使用局部刷新

         如果只是更新某组、某行的话,使用局部刷新。

 

7. 网络数据异步加载,不要阻塞主线程

         如果Cell内的内容来自web,需要加载网络数据,下载图片,配合SDWebImage、YYWebImage等实现异步加载、缓存。不要在主线程做一些文件的I/O操作。

8. 不要动态给cell添加或删除控件

         尽量不要给cell动态添加删除控件,初始化时添加控件,不适用的可以先隐藏。

 

9. 按需加载cell

         cell滚动很快时,只加载范围内的cell,可以提高流畅度。

 

10. 不要实现无用的代理方法,tableView只遵守两个协议

 

11. 圆角优化(点击查看:iOS 控件设置圆角,避免离屏渲染

在APP开发中,圆角图片还是经常出现的。如果一个界面中只有少量圆角图片或许对性能没有非常大的影响,但是当圆角图片比较多的时候就会对性能产生明显的影响,我们设置圆角一般通过如下方式:

 
  1. view.layer.cornerRadius = 5.f;

  2. view.layer.masksToBounds = YES;

  3.  

这样处理的渲染机制是GPU在当前屏幕缓冲区外新开辟一个渲染缓冲区进行工作,也就是离屏渲染,这会给我们带来额外的性能损耗,如果这样的圆角操作达到一定数量,会触发缓冲区的频繁合并和上下文的的频繁切换,性能的代价会宏观地表现在用户体验上——掉帧,点击链接查看如何正确设置圆角。注意一点,单独设置view.layer.masksToBounds = YES;是不会发生离屏渲染的,上面说过尽量不要使用控件透明,如一个标签UILable,尽量用如下方法:

 
  1. label.backgroundColor = [UIColor whiteColor];

  2. label.layer.masksToBounds = YES;

给定一个背景色,然后设置layer的masksToBounds属性为YES,因为如果label的内容是中文,label实际渲染区域要大于label的size,最外层多了一个sublayer,如果不设置,label的边缘外层颜色仍然需要计算,增加了GPU的性能消耗。 

 

12. 不要做多余的绘制工作

         在实现drawRect:的时候,异步绘制,它的rect参数就是需要绘制的区域,这个区域之外的不需要进行绘制。绘制cell不建议使用UIView,建议使用CALayer,从形式来说,UIView的绘制是建立在CoreGraphic上的,使用的是CPU。CALayer使用的是Core Animation,CPU,GPU通吃,由系统决定使用哪个。View的绘制使用的是自下向上的一层一层的绘制,然后渲染。Layer处理的是Texure,利用GPU的Texture Cache和独立的浮点数计算单元加速纹理的处理。从事件的响应来说,UIView是CALayer的代理,layer本身并不能响应事件,因为layer是直接继承自NSObject,不具备处理事件的能力。而UIView是继承了UIResponder的,这也是事件转发的角度上说明,view要比单纯的layer复杂的多。在滑动的列表上,多层次的view再加上各种手势的处理势必导致帧数的下降。

 

13. 预渲染图像

         当新的图像出现时,仍然会有短暂的停顿现象。解决的办法就是在bitmap context里先将其画一遍,导出成UIImage对象,然后再绘制到屏幕。

 

         cell内容经常变动,不要开启光栅化,shouldRasterize,光栅化是将一个layer预先渲染成位图(bitmap),再加入到缓存中,因为tableViewCell的绘制非常频繁,内容在不断的变化,如果使用了光栅化,会造成大量的离屏渲染降低性能。

 

14. 使用正确的数据结构来存储数据

 

15. 尽可能重用开销比较大的对象

         如NSDateFormatter 和 NSCalendar等对象初始化非常慢,我们可以把它加入类的属性当中,或者创建单例来使用。

 

16. 尽量减少计算的复杂度

         在高分屏尽量用ceil、floor或round取整。不要出现1.3,10.333这样的小数。

 

17. 复杂界面建议纯代码

         xib、storyboard虽然方便,但仍然需要系统自动转码,并且对于一些自定义的绘制也有局限性。

 

 

 

猜你喜欢:UITableView简介, UITableView实用练习, MVC与MVVM设计模式简介, 基于MVC模式练习UITabelView使用, 基于MVVM模式练习UITableView使用

 类似资料: