UITableView的优化技巧
来源:互联网 发布:淘宝产品复制 编辑:程序博客网 时间:2024/05/08 05:52
作者:Love@YR
链接:http://blog.csdn.net/jingqiu880905/article/details/51920117
请尊重原创,谢谢!
UITableView的优化是个难点也是个痛点,下面列出各路大神总结的优化技巧。
首先,基础篇:
cell绘制方面cellForRowAtIndexPath:
cell重用 reuseIdentifier
这个可以看下上一篇文章,讲解了不同情况下alloc了多少个cell就比较清楚了。不用reuse你内存受不了。
(也应该在section header和footer中使用reuseIdentifier)统一设计cell
cell的Prototype最好高度抽象统一,越少越好,因为不同的Prototype有其对应的cell重用池,创建的cell种类越多,重用效率也就越低。所以能用一种就用一种。减少视图层级关系,减少subview的数量
尽量少用addView给cell动态添加view,可以初始化时就添加,然后通过hide来控制是否显示。尽量使用opaque
尽量使cell的contentView所有的subview都设置opaque,包括Cell自身。尽量少用或不用透明图层。绘制方法中尽量不要处理过多业务逻辑
高度计算方面heightForRowAtIndexPath:
- row 的高度都一定的情况
删除代理中的heightForRowAtIndexPath: 方法,设置 tableView的rowHeight属性,sectionHeaderHeight footer也同样的道理。
其他:
cell数据资源缓存
一般大家也都会直接用个dictionary或者数组或者model来存下需要显示的数据源,不大可能会到用的时候再去读plist啊,拿数据库里的之类。没啥好说的。UI的尺寸和行高缓存,用 “空间换时间”
正常情况计算行高放在heightForRowAtIndexPath,但如果这个计算很复杂,时间不固定,有时短有时长,你再一reloaddata,或者insertrow,deleterow那就很烦了。
所以将计算行高的时间(当然连同各UI的尺寸计算,你只有算出来所有子view高宽度才能确定cell高度)提前到从服务器拿到数据的时候就开始,计算完了将cell高度,各行各ui的frame一并写回数据源预处理。
一次计算一劳永逸,避免在heightForRowAtIndexPath和cellForRowAtIndexPath方法里每次滚到这一行都去计算。图片预处理
显示之前提前处理。比如缩放:
如果你是在画cell的时候直接设置imageview的 contentMode 属性让 imageview自己缩放,缩放需要对图片做transform ,要对图片乘以一个变换矩阵,计算量很大,很耗性能,更何况你每次滚到那行都要缩放。
所以可以在刚拿到图片就先缩放生成新图,当然最好是服务端能返回相对应大小的图片,这样就不用缩放了。而如果要看大图,最好是点击查看大图的时候再拿大图去渲染。设计时不宜把大图直接显示在列表里。图片异步加载
图片下载放到后台,异步加载。
不过如果每个循环对象都异步加载,很多个线程也会影响主线程的性能,所以好的方法就是按需加载,而不是全部在后台下载图片按需加载
没有在滑动且加速度为0(即已经停止下来时)才去加载图片(不然你滑那么快我每个图都去请求网络下载然后显示,每个下载又是一个子线程,我线程各种切换,然后你刷地又滑回来。。。),且只去画当前可视区域的图片,或者再加上当前可视前后指定的几行。
下载过的不再下载,正在下载的不去下载。
(按需加载可参考上篇文章,苹果官方例子lazyloadImage的解析)图片资源尽量用png,因为iOS本身对png进行了很多优化
关于reloaddata
heightForRowAtIndexPath 在reload data时会执行所有cell高度全部刷新,而不是当前屏幕显示的cell数量。尽量不用reload data而用insertrow deleterow关于重复代码
heightForRowAtIndexPath和cellForRowAtIndexPath中尽量不要有重复代码尽量少用xib storyboard创建cell,他们需要系统自动转码,也不能自定义cell绘制
其次,关于cell上图片圆角
- cornerRadius方式
cell.myImageView.layer.cornerRadius = 8.0; cell.myImageView.layer.masksToBounds = YES;//或者cell.myImageView.clipsToBounds = YES
这种方式会触发离屏渲染。iOS9之后png图片不会了。
- 设置masklayer
CAShapLayer *layer = [CAShapLayer layer];UIBezierPath *bzpath = [UIBezierPath bezierPathWithOvalInRect:imageview.bounds] ;layer.path = bzpath.CGPath;cell.myImageView.layer.mask = layer
一次mask发生两次离屏渲染和一次主屏渲染,比上面的方法效率还低。(主要是上下文切换耗性能)
但是可不局限于圆角,由 mask 控制边角显示为什么形状。
- 光栅化,也叫位图化
cell.myImageView.layer.cornerRadius = 8.0; cell.myImageView.layer.masksToBounds = YES;cell.myImageView.layer.shouldRasterize = YES;cell.myImageView.layer.rasterizationScale = [UIScreen mainScreen].scale;//UIImageView不加这句会产生一点模糊
设置光栅化,可以使离屏渲染的结果缓存到内存中存为位图,使用的时候直接使用缓存,节省了一直离屏渲染损耗的性能。但是如果layer及sublayers常常改变的话,它就会一直不停的渲染及删除缓存重新创建缓存,此情况下建议不要使用光栅化,比较损耗性能
- 对图片进行切角
imageview的drawRect或者setImage里
UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, [UIScreen mainScreen].scale);[[UIBezierPath bezierPathWithRoundedRect:self.bounds cornerRadius:self.bounds.size.height/2] addClip];[imageFromServer drawInRect:self.bounds];circleImage = UIGraphicsGetImageFromCurrentImageContext();UIGraphicsEndImageContext();[super setImage:circleImage]
切角的操作是在CPU内完成的,而后我们只需要取到处理完成的bitmap(可为UIImage对象)交给GPU显示于屏幕即可。
无离屏渲染,把 GPU的压力转给 CPU,适用于 CPU 压力不大的情况,CPU和内存消耗增大。
- mask图和原图合成
UIImage *roundedImage = [self imageByComposingImage:image withMaskImage:[UIImage imageNamed:@"mask.png"]]; cell.myImageView.image = roundedImage;- (UIImage *)imageByComposingImage:(UIImage *)image withMaskImage:(UIImage *)maskImage { CGImageRef maskImageRef = maskImage.CGImage; CGImageRef maskRef = CGImageMaskCreate(CGImageGetWidth(maskImageRef), CGImageGetHeight(maskImageRef), CGImageGetBitsPerComponent(maskImageRef), CGImageGetBitsPerPixel(maskImageRef), CGImageGetBytesPerRow(maskImageRef), CGImageGetDataProvider(maskImageRef), NULL, false); CGImageRef newImageRef = CGImageCreateWithMask(image.CGImage, maskRef); CGImageRelease(maskRef); UIImage *newImage = [UIImage imageWithCGImage:newImageRef]; CGImageRelease(newImageRef); return newImage;}
不局限圆角,但效率比较低。
- 直接覆盖一张中间为圆形透明的图片
直接[imageview addsubview:中间透明的图片]
然后再[imageview setImage:原图]
这种方法就是多加了一张透明的图片,GPU计算多层的混合渲染blending也是会消耗一点性能的
最后,关于Instrument来调试tableView性能
Core Animation
debug options选择:
Color Hits Green and Misses Red:
光栅化对应的渲染结果会被缓存。如果图层是绿色,就表示这些缓存被复用;如果是红色就表示缓存会被重复创建,这就表示该处存在性能问题了。Color Offscreen-Rendered Yellow开启后会把那些需要离屏渲染的图层高亮成黄色,这就意味着黄色图层可能存在性能问题。
Color misaligned images:
洋红色代表像素没对齐???
黄色代表图片有缩放
解决方法是设置各view 的 frame 时用整数不用小数??
和最好刚拿到图时就缩放生成新图而不是显示时再缩放GPU Driver
Renderer Utilization
如果这个值超过了~50%,就意味着你的动画可能对帧率有所限制,很可能因为离屏渲染或者是重绘导致的过度混合Tiler Utilization
如果这个值超过了~50%,就意味着你的动画可能限制于几何结构方面,也就是在屏幕上有太多的图层占用了。帧率Core animation frames per seconds 越接近60滑动越顺畅。
time profiler
Call Tree那边设置全选,去查看哪句代码走的时间比较长
参考:http://tutuge.me/2015/02/19/提升UITableView性能-复杂页面的优化/
http://blog.csdn.net/hanmingsa/article/details/51648199
- UITableView的优化技巧
- UITableView的优化技巧
- UITableView的优化技巧
- 优化UITableView的几个技巧
- iOS --- UITableView的优化技巧
- UITableView优化技巧
- UITableView优化技巧
- UITableView优化技巧
- UITableView优化技巧
- UITableView优化技巧
- UITableView优化技巧
- [iOS]UITableView优化技巧
- UITableView优化技巧
- UITableView优化技巧
- UITableView优化技巧
- UITableView优化技巧
- UITableView优化技巧
- UITableView优化技巧
- Linux系统管理命令之accton的使用
- IOS 中视频和音乐合成
- IOS 中视频和音乐合成
- View 事件分发机制
- 我的博客迁移啦!!!!
- UITableView的优化技巧
- 哈理工OJ 1430 神秘植物(矩阵快速幂+矩阵构造)
- 性能分析之内存优化
- 配置Ubuntu下MXnet 所需的 Python环境
- jdk里面找不到tools.jar和dt.jar
- NYOJ-最大的数
- 快速找中值
- 面试题(一)-sql查询重复数据
- Java 对象比较器 comparator 的作用及用法