渲染流程

1、 Application 中布局 UIKit 视图控件间接的关联 Core Animation 图层
2、Core Animation 图层相关的数据提交到 iOS Render Server,即 OpenGL ES & Core Graphics
3、Render Server 将与 GPU 通信把数据经过处理理之后传递给 GPU
4、GPU 调⽤用 iOS 当前设备渲染相关的图形设备 Display
我们则是要在Commit Transaction这个过程中进行优化。那我们优化为什么要在Commit Transaction这个过程中呢?且听我娓娓道来。
Commit Transaction做了什么?
在Commit Transaction一共做了4件事情。分别是:
1、Layout:构建视图,frame,遍历的操作[UIView layerSubview],[CALayer layoutSubLayers]
2、Display:绘制视图 , display —- drawReact(),displayLyaer:(位图的绘制)
3、Prepare:额外的 Core Animation ⼯工作,比如解码(加载图片 )
4、Commit:打包图层并将它们发送到 Render Server
卡顿产生的原因

我们的CPU会等待VSync的信号,信号来了,CPU才会进行新一帧的渲染和缓存区的更新。如果VSync的信号到来的时候,而GPU没有办法完成新一帧的渲染和缓存区的更新时, 就会产生掉帧的现象。也就是我们所看见的卡顿。而优化共分为预排版、预渲染和预解码
界面优化实践(以一个列表页为例)

效果如图(临时写的,请忽略好不好看的问题)。通常我们在网络请求完成,解析完数据,就会回到主线程直接reloadData。数据较少的页面一般来说卡顿不会很明显,但是一次性往下滑N个数据的时候,滑动速度变慢,就会出现卡顿情况(如果是在heightForRowAtIndexPath这个方法里面动态计算高度,卡顿会更明显)。此时我们优化的步骤分为3步。
- 预排版:将原本由CPU进行布局计算,交给异步子线程中提前计算好布局,回到主线程中,布局当前页面的更新。

首先我们创建一个叫DemoCellLayout的类(继承于NSObject),然后在解析完Model之后,调用layout中自己的方法:initWithModel去进行计算。
DemoCellLayout.h:

DemoCellLayout.m: 首先初始化类和Model的赋值
- (instancetype)initWithModel:(Model *)dataModel{
if (!dataModel) return nil;
self = [super init];
if (self) {
_dataModel = dataModel;
[self layout];
}
return self;
}
-(void)setDataModel:(Model *)dataModel{
_dataModel = dataModel;
[self layout];
}
计算title的Ract,此时我们会把计算好的layoue返回并存储到layoutArr里面。

我们在Cell里面就直接给title的frame赋值。


- 预渲染:当遇到特殊的图片时(比如圆角)就可以在渲染的过程当中进行处理。特别是容易触发离屏渲染的时候。这里以切割圆角为例。
很多人在做圆角的时候,都是直接用masksToBounds和cornerRadius来进行切割的,那为什么会产生离屏渲染呢?因为此时对于圆角的处理是在主线程当中进行的。就相当于Commit Transaction的步骤做了两次,而且是由CPU+GPU共同完成的。那我们该如何优化呢?其实SDWebImage在就帮我们准备好了API。
- (void)sd_setImageWithURL:(nullable NSURL *)url
placeholderImage:(nullable UIImage *)placeholder
options:(SDWebImageOptions)options
completed:(nullable SDExternalCompletionBlock)completedBlock;

- 预解码:当图片由磁盘加载到内存当中的时候,是由UIImage加载Data Buffer(进过压缩算法所得到的二进制数据),然后进行解码的操作,变成Image Buffer(像素缓冲区),然后在渲染图片。如果存在大量图片要进行解码,GPU就要将大量的时间放在解码上,这时候就有可能发生掉帧的现象。此时我们就可以在CPU中进行解码的操作。其实这一步,SDWebImage、YYImage显示图片时,本质上和预解码是一样的。
总结
预排版、预渲染、预解码的性质都是一样的,将耗时的操作,都用一个子线程去预先做好,然后在GPU上直接赋值,减少GPU的负担。下载链接