在iOS开发中,保持界面流畅和良好的用户体验是很是重要的,那么界面的优化对咱们来讲就是一个老生常谈的话题,这里总结了网上的一些资料和本身的一些开发经验,讲讲界面性能的一些技巧.缓存
VSync
垂直信号,咱们通常说页面滑动的流畅是60FPS,指的就是每秒钟会有60 帧的画面更新,咱们在人眼上看到的就是流畅的效果,那基于此呢,每一个16.7ms(1/60s)就要产生一帧画面,那么在这16.7ms中就须要由CPU和GPU共同协同完成产生最终的一帧的数据并经过数模转换传递给显示器显示. 其中CPU负责计算显示内容,好比视图的建立、布局计算、图片解码、文本绘制等,随后把产生的位图提交给GPU,再由GPU进行图层的合成、纹理渲染等.而后GPU将渲染结果提交到帧缓冲区去,等待下一次 VSync 信号到来时显示到屏幕上.而若是这个时候CPU或GPU处理的时间过长,当下一次VSync到来的时候,GPU尚未将渲染结果提交到帧缓冲区中去,那么这一帧就会被丢弃,画面继续保持以前的内容,等下下次VSync信号到来的时候(若是此时GPU已将渲染结果提交到了帧缓冲区中),再显示并刷新界面.因此原本16.7ms后就该更新一帧画面的,如今用了33.4ms(可能更多),这就是卡顿产生的缘由. 那么界面优化就主要从CPU和GPU两个层面来优化了.
视图的布局计算是界面优化的一个重点,通常对于视图布局的优化能够经过提早计算好视图布局和对视图布局进行缓存.好比cell的高度、label的行高等涉及到计算的.对于比较复杂的界面,若是使用Autolayout
也会带来性能上的问题,Masonry
这个库就是基于Autolayout
.由于过多的约束带来的计算量是很是大的,给CPU的消耗很是大.那么这就容易形成CPU运算时间超时产生卡顿.若是用Frame
的话会好不少.这篇文章比较了Frame和Autolayout对性能的影响. 可使用SDAutoLayout
这个库,它的布局是基于Frame的,或者使用 ComponentKit、AsyncDisplayKit 等框架.一样Ulabel的宽高和绘制方法[NSAttributedString boundingRectWithSize:options:context:]
和 [NSAttributedString drawWithRect:options:context:]
也能够放在后台线程进行以免阻塞主线程.bash
对象的建立会分配内存、调整属性、甚至还有读取文件等操做,比较消耗 CPU 资源,因此将对象的建立放到子线程中去作会提升部分性能.经过 Storyboard 建立视图对象还会涉及到文件反序列化操做,比起代码建立的视图,Storyboard要消耗更多的资源,因此对于一些对性能比较高的界面最好是经过代码来进行建立和布局.若是没有涉及到触摸事件等操做,能够用CALayer替代UIView,由于CALayer相对于UIView来讲要轻量不少.框架
如上图所示,屏幕上能看到的全部文本内容控件,包括 UIWebView,在底层都是经过 CoreText 排版、绘制为 Bitmap 显示的。常见的文本控件 (UILabel、UITextView 等),其排版和绘制都是在主线程进行的,当显示大量文本时,CPU 的压力会很是大.因此能够自定义控件,直接使用 CoreText 进行排版控制,不过这样太麻烦了,反正我不会这么作,哈哈哈.async
图像的绘制是须要消耗CPU资源的,将绘制过程放在后台线程中进行,而后再在主线程中将结果设置到layer的contents中,这样会提升CPU的效率.代码以下:布局
- (void)display {
dispatch_async(backgroundQueue, ^{
CGContextRef ctx = CGBitmapContextCreate(...);
CGImageRef img = CGBitmapContextCreateImage(ctx);
CFRelease(ctx);
dispatch_async(mainQueue, ^{
layer.contents = img;
});
});
}
复制代码
图像的绘制一般是指用那些以 CG 开头的方法把图像绘制到画布中,而后从画布建立图片并显示的过程。前面的模块图里介绍了 CoreGraphic 是做用在 CPU 之上的,所以调用 CG 开头的方法消耗的是 CPU 资源。咱们能够将绘制过程放到后台线程,而后在主线程里将结果设置到 layer 的 contents 中.性能
相对于 CPU 来讲,GPU 能干的事情比较单一:主要也就是纹理(图片)和形状(三角模拟的矢量图形)两类。优化
全部的 Bitmap,包括图片、文本、栅格化的内容,最终都要由内存提交到显存,绑定为 GPU Texture。不管是提交到显存的过程,仍是 GPU 调整和渲染 Texture 的过程,都要消耗很多 GPU 资源。当在较短期显示大量图片时(好比 TableView 存在很是多的图片而且快速滑动时),CPU 占用率很低,GPU 占用很是高,界面仍然会掉帧.spa
当多个视图(或者说 CALayer)重叠在一块儿显示时,GPU 会首先把他们混合到一块儿。若是视图结构过于复杂,混合的过程也会消耗不少 GPU 资源。为了减轻这种状况的 GPU 消耗,应用应当尽可能减小视图数量和层次,并在不透明的视图里标明 opaque 属性以免无用的 Alpha 通道合成。固然,这也能够用上面的方法,把多个视图预先渲染为一张图片来显示.线程
CALayer 的 border、圆角、阴影、遮罩(mask),CASharpLayer 的矢量图形显示,一般会触发离屏渲染(offscreen rendering),而离屏渲染一般发生在 GPU 中。当一个列表视图中出现大量圆角的 CALayer,而且快速滑动时,能够观察到 GPU 资源已经占满,而 CPU 资源消耗不多。这时界面仍然能正常滑动,但平均帧数会降到很低。为了不这种状况,能够尝试开启 CALayer.shouldRasterize 属性,但这会把本来离屏渲染的操做转嫁到 CPU 上去。对于只须要圆角的某些场合,也能够用一张已经绘制好的圆角图片覆盖到本来视图上面来模拟相同的视觉效果。最完全的解决办法,就是把须要显示的图形在后台线程绘制为图片.设置了如下属性时,都会触发离屏渲染:code
layer.shouldRasterize
,光栅化
layer.mask
,遮罩
layer.allowsGroupOpacity
为YES,layer.opacity
的值小于1.0
layer.cornerRadius
,而且设置layer.masksToBounds
为YES
。可使用剪切过的图片,或者使用layer画来解决
layer.shadows
,(表示相关的shadow开头的属性),使用shadowPath
代替