iOS 高级面试--UI视图
作者:互联网
UlTableView 相关
一,重用机制
1.iOS如何实现cell的重用机制?
A1-A7
使用相同的identifer
,当tableView
向上滑动,A1
划出页面后,就被放入了重用池。- 当
A7
即将展示时,首先会在重用池中查看时候有相同identifer
的cell
可以被重用,如果有则直接取出使用,若无则创建一个新的cell
。
2.如何手动实现重用机制?
ViewReusePool
类的声明
ViewReusePool
类的实现
dequeueReusableView
函数实现
addUsingView:
函数实现
reset
函数实现
ViewReusePool
类的使用
二,数据源同步问题
- 当数据源在
主线程
中有删除
操作,同时在子线程
上又有加载更多
数据的操作时,就会出现数据源同步问题
。
1.数据源同步解决方案
a,并发访问、数据拷贝
子线程
返回主线程
的数据中,仍然包含删除的这一条数据。
- 在
主线程
进行删除操作时,将操作记录下来。之后在子线程
同步数据时,同步删除操作。
b.串行访问 - 将
子线程
的数据同步和主线程
的删除操作全部放入一个串行队列
中执行。 - 删除动作可能会有延时。
事件传递&视图相应
一,UIView和CALayer
1.UIView和CALayer的关系和区别?
a.关系
UIView
对象中的layer
指向一个CALayer
变量UIView
对象中的backgroundColor
属性,是对CALayer
同名属性的封装。UIView
展示部分是由CALayer
中的contents
来决定。contents
对应的backing store
其实是一个bitmap
的位图。- b.区别
UIView
为其提供内容,以及负责处理触摸等事件,参与响应链。CALayer
负责显示内容contents
。
2、为什么UIView负责触摸事件,CALayer负责显示?
- 设计模式,
单一职责
原则。
二、事件传递与视图响应链
1、当点击View C2区域,系统是如何找到响应视图的呢?
a.事件传递的流程
- 当用户点击屏幕,事件会被
UIApplication
接受,并传递给UIWindow
。 UIWindow
调用hitTest
函数,在hitTest
内调用pointInside
判断事件是否在该视图内。- 若为
false
,则返回该视图,事件传递流程结束。 - 若为
true
,则可倒叙遍历
该视图的子视图
,并调用子视图
的hitTest
函数。 - 找到最终
hitTest
为true
的子视图
,并依次返回,事件传递流程结束。
b.hitTest
系统内部实现
- 在当前视图子视图调用
hitTest
函数前,需要将当前坐标转换为子视图
中的坐标。
2,如何只让方形图片的圆形区域接受事件响应?
- 重写视图的
pointInside
函数,使得点击区域在圆形范围内返回true
,否则返回false
。
3,视图响应流程
a,事件的响应是通过响应链来传递的。
UIView
通过继承UIResponder
,拥有以下函数
b,事件传递之后由谁来响应?
- 如果响应视图无法处理响应事件,则响应事件会通过
响应链
传递给父视图
尝试处理,直到传递给UIApplication
。 如果传递给UIApplication依然没有处理响应事件,则事件将被忽略。
图像显示原理
一、图像显示流程
CPU
和GPU
是通过事件总线
链接在一起的。CPU
输出的位图
,在适当时机由事件总线
上传给GPU
。GPU
会对位图
进行渲染
,然后将结果放入帧缓冲区。视频控制器
通过Vsync信号
,在指定时间(16.7ms
)之前,从帧缓冲区
中提取屏幕显示内容,然后显示在显示器上。
二,UI视图显示过程
- 当创建一个
UIView
对象,它的显示部分由CALayer
来控制的。 CALayer
有一个contents
属性,就是最终绘制到屏幕上的位图
。- 在绘制
contents
内容时,系统会回调drawRect:
函数,我们可以在函数中增加绘制内容。 - 绘制好的
位图
会通过Core Animation
框架,最终经由GPU
当中的OpenGL
渲染管线,渲染在屏幕上。
三、CPU工作过程
1,Layout
- UI布局(
frame
设置) - 文本计算(
size
计算)
2,Display
- 绘制(
drawRect:
)
3,Prepare
- 图片编解码
4,Commit
- 提交位图
四、GPU渲染管线过程
卡顿&掉帧的原因
- 按照每秒
60FPS
刷新率,每隔16.7ms
就会有一次Vsync
信号。 - 在
16.7ms
内,需要CPU
和GPU
协同产生这一帧的画面,并在下一次Vsync
信号来临时,显示这一帧的画面。 - 如果
CPU
和GPU
的工作时长超过16.7ms
,那么当Vsync
信号来临时,无法提供这一帧的画面,就会出现掉帧现象。 - 上一帧没有显示的画面,会在下一次
Vsync
信号来临时显示。
一,滑动优化方案
1、CPU
- 对象
创建
、调整
、销毁
可以放在子线程。 - 预排版(布局计算、文本计算)操作,可以放在子线程操作。
- 预渲染(文本等
异步绘制
、图片编解码
等)操作,降低CPU的耗时。
2、GPU
- 避免
离屏渲染
,降低纹理渲染的耗时。 - 如果
视图层级复杂
,GPU在视图合成时会做大量的计算。可以通过异步绘制
等机制,减少视图层级,减轻GPU的压力。
绘制原理&异步绘制
一,UIView的绘制原理
当调用setNeedsDispaly
函数,实际是调用view.layer
的setNeedsDispaly
函数。
该函数会将layer
标记,在runloop
即将结束时,调用CALayer display
函数,进入当前视图的真正绘制。
在CALayer display
函数中,会判断它的代理是否响应displayLayer:
函数,如果YES
,则可进行异步绘制
,否则进入系统绘制流程
。
二,系统的绘制流程
layer
会创建一个backing store
,在drawRect:
函数中可以拿到这个上下文。layer
会判断是否有代理,如果有代理,在系统内部绘制完成后,会调用UIView drawRect:
,允许在系统绘制基础上,进行增添修改。- 最终由
CALayer
上传backing store
到GPU
。
三,异步绘制
- 如果
layer
存在代理,则由代理执行display:
函数生成位图,并设置该bitmap
作为layer.contents
属性。
离屏渲染
在屏渲染(On-Screen Rendering)
意为当前屏幕渲染,指的是GPU
的渲染操作是在当前用于显示屏幕缓冲区中进行。离屏渲染(Off-Screen Rendering)
意为离屏渲染,指的是GPU
的再当前屏幕缓冲区以外新开辟
一个缓冲区进行渲染操作。
一,什么场景会触发离屏渲染?
- 圆角(需要和maskToBounds一起使用)
- 图层蒙版
- 阴影
- 光栅化
二,为什么要避免离屏渲染?
- 创建新的渲染
缓冲区
,会有内存上的开销。 - 多通道渲染管线,最终需要合成,会涉及
上下文切换
,增加GPU
的开销。 - 总结:离屏渲染会增加
GPU
的处理时间,这样可能导致CPU
+GPU
的总处理时间超过16.7ms
,从而出现掉帧卡顿
的现象。
UI视图面试总结
- 系统的UI事件传递机制是怎样的?
- 考察
hitTest
和pointInside
内部实现。
- 考察
- 使UITableView滚动更流畅的方案或思路都有哪些?
CPU方面
,在子线程进行对象的创建、调整、销毁、预排版、图片异步绘制
。
- 什么是离屏渲染?
- 在当前屏幕缓存区外,新开辟一个缓冲区进行渲染。
- UIView 和 CALayer之间的关系是怎样的?
UIView
负责事件传递
和事件响应
。CALayer
负责UI视图显示
。- 使用到设计模式六大设计原则中的
单一职责
原则。
源文作者:逍遥归来
链接:https://juejin.cn/post/6899772676794122253
标签:渲染,--,iOS,视图,CALayer,GPU,绘制,UIView 来源: https://blog.csdn.net/qq_45765503/article/details/110541521