iOS高级资深工程师面试篇系列 - 已更新3篇
UI部分1/3 -UITableView-事件传递&视图响应
UI部分2/3 -图像显示原理-UI卡顿&掉帧
UI部分3/3 -UIView绘制原理-离屏渲染
技术:iOS底层原理、事件传递、视图响应、图像显示原理、UI卡顿&掉帧、UIView绘制原理、离屏渲染
高级、资深工程师的考点之中
请你说说UIView绘制的原理
- 当我们调用UIView的
setNeedsDisplay
方法时候。
系统会立即调用[view.layer setNeedsDisplay]
方法。相当于在layer上面打上了一个脏标记。
然后再RunLoop即将结束的时候。才会调用[CALayer display]
方法
然后才进入我们视图真正绘制工作流程当中。- 所以我们可以回答说,为什么UIView的调用
setNeedsDisplay
方法并没有立刻发生对应视图的绘制工作。
实际上是。当RunLoop将要结束的时候。才会开始进入视图绘制流程当中。- CALayer的delegate是否会响应
displayLayer
。如果不响应 就进入系统绘制流程
如果CALayer的delegate 响应了displayLayer
方法的时候。 就会进入异步绘制入口
。
对于系统内部绘制流程
- 内部会创建一个 CALayer creates
backing store
(CGContextRef)
我们可以理解为CGContextRef
,CGContextRef方法当中可以通过上下文获取取出栈顶的context,拿到的就是当前控件的上下文或者是backing store- 然后layer会进行判断。它
是否有代理
如果没有
就会调用[CALayer drawInContext]
如果有
就会调用[layer.delegate drawLayer:inContext:]
- 然后做当前视图的绘制工作 - 发生在系统内部绘制当中的
- 然后再合适的时机给予我们的一个回调方法
[UIView drawRect:]
- drawRect默认实现 其实就是什么都不做的
系统给我们开drawRect的铐子。其实就是允许我们在系统绘制的基础之上。做一些其他的绘制工作。
不管哪两个分支。最终都是由CALayer的backing store 上传到 GPU
最终就结束系统默认的流程
如果layer.delegate 遵循了
displayLayer
方法
或者实现了displayLayer
这个方法。我们就可以进入异步绘制流程当中
在这个异步绘制流程当中。需要代理去负责生成对应的bitmap
,也就是我们所对应的位图
同时需要 设置bitmap 作为layer.contents属性的值
我们通过上方一个时序图来了解异步绘制机制和流程
- 左侧是一个主队列,右侧是一个全局并发队列
- 假如说 在某个时刻调用了
setNeedsDisplay
方法之后呢- 那么,在当前RunLoop将要结束之后,就会由
系统
调用 视图所对应Layer的[CALayer display]
方法,然后如果我们的代理
实现了displayLayer
这个函数的时候,就会调用我们代理的displayLayer的这个函数方法- 然后通过
子线程
的切换。在子线程当中,去做一个位图的绘制
全局并发队列做的操作
CGBitmapContextCreate()
作用: 通过CGBitmapContextCreate()
这个函数创建一个位图的上下文
CoreGraphic API
作用: 通过CoreGraphic
相关的API
,可以做一些当前UI控件视图的绘制工作
CGBitmapContextCreateImage()
作用: 通过CGBitmapContextCreateImage()
函数 ,根据当前所绘制的上下文生成一张CGImage的图片
。然后再回到主队列
当中- 主线程可以做其他的工作
- 等到子线程返回一张CGImage的图片到主队列当中。提交位图,设置给 [CALayer setContents:]属性。
这样就完成了一个UI控件的异步绘制过程
微博、美团真机面试问题
3. 我们在什么场景下会触发离屏渲染触发? 4.为何要避免离屏渲染有离屏渲染就有在屏渲染
当前屏幕渲染
- On-Screen Rendering
意为当前屏幕渲染,指的是
GPU
的渲染操作实在当前用于显示的屏幕缓冲区中进行
在屏渲染指的是GPU层面的一个概念
Off-Screen Rendering
意为离屏渲染,指的是GPU在当前屏幕缓冲区以外
新开辟
一个缓冲区进行渲染操作
离屏渲染本身这个概念,起于GPU层面上面。
通俗语言来说明 离屏渲染:
- 也就是当我们设置视图的图层属性。如果在指定未愈合之前,不能用于直接显示的时候。就触发了离屏渲染。
典型的离屏渲染
,设置视图的圆角属性,包括一些蒙层的遮罩。- 当我们指定UI视图的某些属性,标记它为未愈合成之前,不能用于当前屏幕上直接显示的时候。就会触发了离屏渲染
- 离屏渲染的概念,起源于GPU层面上面:
指的是GPU在当前屏幕缓冲区以外新开辟
一个缓冲区进行渲染操作
初级、中级工程师会问到
- 圆角(当和maskToBounds一起使用时)
只有同时设置圆角和maskToBounds为YES的时候,才会触发离屏渲染- 图层蒙版
- 阴影
- 光栅化
- CPU和GPU在进行具体视图绘制渲染的时候。做了大量的工作
- 离屏渲染发生在GPU层面上面的。
- 由于离屏渲染使GPU层面上面,触发了
OpenGL的多通道渲染管线
。产生了额外的开销
。所以我们需要避免离屏渲染
面试可以这样回答
:
高级、资深工程师的回答
在触发离屏渲染会增加GPU的工作量。
增加GPU的工作量。很有可能导致CPU和GPU工作耗时。加起来的总耗时大于16.7ms
可能就回到导致UI的卡顿和掉帧
所以我们需要避免离屏渲染
初、中级工程师的回答
- 创建新的渲染缓冲区 - 存在着内存上的开销
- 上下文切换 - 因为有多通道渲染管线。需要把多通道的渲染结果做一个最终的合成。涉及到上下文切换。就有一个GPU的额外的开销
考察的是hitTest 和pointInside的实现
高级、资深问题
涉及到 性能优化的掌握程度。
可以从CPU或者GPU的考虑层面上进行说明。
对于CPU可以采用在子线程进行对象的创建、销毁。包括预排版、以及图片的解码。和采用异步绘制方案。
都是源于关于基于UI卡顿和掉帧的分析
层面上
离屏渲染概念起源于GPU
在GPU层面上。如果在当前屏幕缓冲区以外新开辟
一个缓冲区进行渲染操作
就是离屏渲染为什么要避免离屏渲染?
UIView是专门做UI传递和事件响应的
CALayer完全是负责View的视图显示工作
这里包含一个六大原则中的一个
单一原则
- 涉及到一个面试题
为什么要区分UIView和CALayer的工作