iPhone上面的应用一直都是以流畅的操作体验而著称,但是由于之前开发人员把注意力更多的放在开发功能上面,比较少去考虑性能的问题,可能这其中涉及到objective-c,c++跟lua,优化起来相对复杂一些,导致应用在比如touch等较低端的产品上,光从启动到进入页面就花了将近一分钟的时间,页面之间的切换没有那种很流畅的感觉,内存也居高不下,比较影响应用的用户体验。下面就常州开发APP公司来具体分析,iOS APP流畅问题优化技巧等:
产生卡顿的原因:
系统中 CPU、GPU、显示器是以上面这种方式协同工作的。CPU 计算好显示内容提交到 GPU,GPU 渲染完成后将渲染结果放入帧缓冲区,随后视频控制器会按照 VSync 信号逐行读取帧缓冲区的数据,经过可能的数模转换传递给显示器显示。
iOS 设备中的 CPU & GPU
CPU
加载资源,对象创建,对象调整,对象销毁,布局计算,Autolayout,文本计算,文本渲染,图片的解码, 图像的绘制(Core Graphics)都是在CPU上面进行的。
GPU
GPU是一个专门为图形高并发计算而量身定做的处理单元,比CPU使用更少的电来完成工作并且GPU的浮点计算能力要超出CPU很多。
GPU的渲染性能要比CPU高效很多,同时对系统的负载和消耗也更低一些,所以在开发中,我们应该尽量让CPU负责主线程的UI调动,把图形显示相关的工作交给GPU来处理,当涉及到光栅化等一些工作时,CPU也会参与进来,这点在后面再详细描述。
相对于CPU来说,GPU能干的事情比较单一:接收提交的纹理(Texture)和顶点描述(三角形),应用变换(transform)、混合(合成)并渲染,然后输出到屏幕上。通常你所能看到的内容,主要也就是纹理(图片)和形状(三角模拟的矢量图形)两类。
CPU 和 GPU 的协作
由上图可知,要在屏幕上显示视图,需要CPU和GPU一起协作,CPU计算好显示的内容提交到GPU,GPU渲染完成后将结果放到帧缓存区,随后视频控制器会按照VSync信号逐行读取帧缓冲区的数据,经过可能的数模转换传递给显示器显示。
优化方案
在YY大神的 iOS 保持界面流畅的技巧中详细介绍了 CPU 资源消耗原因和解决方案和 GPU 资源消耗原因和解决方案,这里面包括了开发中的大部分场景,可以帮助我们快速定位卡顿的原因,迅速解决卡顿。
下面是一些常见的优化方案!
TableViewCell 复用
在cellForRowAtIndexPath:回调的时候只创建实例,快速返回cell,不绑定数据。在willDisplayCell: forRowAtIndexPath:的时候绑定数据(赋值)。
高度缓存
在tableView滑动时,会不断调用heightForRowAtIndexPath:,当cell高度需要自适应时,每次回调都要计算高度,会导致 UI 卡顿。为了避免重复无意义的计算,需要缓存高度。
怎么缓存?
字典,NSCache。
UITableView-FDTemplateLayoutCell
视图层级优化
不要动态创建视图
在内存可控的前提下,缓存subview。
善用hidden。
减少视图层级
减少subviews个数,用layer绘制元素。
少用clearColor,maskToBounds,阴影效果等。
减少多余的绘制操作
图片
不要用JPEG的图片,应当使用PNG图片。
子线程预解码(Decode),主线程直接渲染。因为当image没有Decode,直接赋值给imageView会进行一个Decode操作。
优化图片大小,尽量不要动态缩放(contentMode)。
尽可能将多张图片合成为一张进行显示。
减少透明 view
使用透明view会引起blending,在iOS的图形处理中,blending主要指的是混合像素颜色的计算。最直观的例子就是,我们把两个图层叠加在一起,如果第一个图层的透明的,则最终像素的颜色计算需要将第二个图层也考虑进来。这一过程即为Blending。
会导致blending的原因:
UIView的alpha<1。
UIImageView的image含有alpha channel(即使UIImageView的alpha是1,但只要image含有透明通道,则仍会导致blending)。
为什么blending会导致性能的损失?
原因是很直观的,如果一个图层是不透明的,则系统直接显示该图层的颜色即可。而如果图层是透明的,则会引起更多的计算,因为需要把另一个的图层也包括进来,进行混合后的颜色计算。
opaque设置为YES,减少性能消耗,因为GPU将不会做任何合成,而是简单从这个层拷贝。
减少离屏渲染
离屏渲染指的是在图像在绘制到当前屏幕前,需要先进行一次渲染,之后才绘制到当前屏幕。
OpenGL中,GPU屏幕渲染有以下两种方式:
On-Screen Rendering即当前屏幕渲染,指的是GPU的渲染操作是在当前用于显示的屏幕缓冲区中进行。
Off-Screen Rendering即离屏渲染,指的是GPU在当前屏幕缓冲区以外新开辟一个缓冲区进行渲染操作。
为什么离屏渲染会发生卡顿?主要包括两方面内容:
创建新的缓冲区。
上下文切换,离屏渲染的整个过程,需要多次切换上下文环境(CPU渲染和GPU切换),先是从当前屏幕(On-Screen)切换到离屏(Off-Screen);等到离屏渲染结束以后,将离屏缓冲区的渲染结果显示到屏幕上又需要将上下文环境从离屏切换到当前屏幕。而上下文环境的切换是要付出很大代价的。
设置了以下属性时,都会触发离屏渲染:
layer.shouldRasterize,光栅化
layer.mask,遮罩
layer.allowsGroupOpacity为YES,layer.opacity的值小于1.0
layer.cornerRadius,并且设置layer.masksToBounds为YES。可以使用剪切过的图片,或者使用layer画来解决。
layer.shadows,(表示相关的shadow开头的属性),使用shadowPath代替。
两种不同方式来绘制阴影: 不使用shadowPath
![](https://i-blog.csdnimg.cn/blog_migrate/02f47054fc19761c373e94420a55449e.png)
离屏渲染的优化建议
使用ShadowPath指定layer阴影效果路径。
使用异步进行layer渲染(Facebook开源的异步绘制框架AsyncDisplayKit)。
设置layer的opaque值为YES,减少复杂图层合成。
尽量使用不包含透明(alpha)通道的图片资源。
尽量设置layer的大小值为整形值。
直接让美工把图片切成圆角进行显示,这是效率最高的一种方案。
很多情况下用户上传图片进行显示,可以在客户端处理圆角。
使用代码手动生成圆角image设置到要显示的View上,利用UIBezierPath(Core Graphics框架)画出来圆角图片。
合理使用光栅化 shouldRasterize
光栅化是把GPU的操作转到CPU上,生成位图缓存,直接读取复用。
优点:
CALayer会被光栅化为bitmap,shadows、cornerRadius等效果会被缓存。
缺点:
更新已经光栅化的layer,会造成离屏渲染。
bitmap超过100ms没有使用就会移除。
受系统限制,缓存的大小为 2.5X Screen Size。
shouldRasterize适合静态页面显示,动态页面会增加开销。如果设置了shouldRasterize为YES,那也要记住设置rasterizationScale为contentsScale。
异步渲染
在子线程绘制,主线程渲染。例如 VVeboTableViewDemo
![](https://i-blog.csdnimg.cn/blog_migrate/f4b29234a27f2fad22c2c38737e89dc3.png)