iOS高手

iOS面试之UI视图模块

2019-11-20  本文已影响0人  木子心语

UI视图内容如下:

1.UItableView
2.卡顿/掉帧
3.绘制原理/异步绘制
4.图像显示原理
5.事件传递/视图响应
6.离屏渲染


UI视图.png

1.UITableView

为什么要用重用机制?
UITableView是可以滚动的一个控件,当UItableView回滚时,如果不用重用
机制会重复初始化原来已初始化的cell,所以用重用机制会节省性能,避免
出现一些网络因素而造成的卡顿现象.
主线程.png
1.比如一组数据源
2.我们突然想删除某条数据,删除操作必须在主线程中完成,这个时候问题就来了,如何同步数据源?
数据源同步1.png 数据源同步2.png
数据源同步解决方案
1.并发访问,数据拷贝

案例:主线程与子线程(如上图)
- 数据拷贝在主线程当中,拷贝的结果会给子线程使用,进行网络请求
- 在子线程进行网络请求,数据解析的过程中,我们在主线程中删除一行数据,刷新UI,这条删除的数据就不存在了
- 如果还有时间,主线程做其他的工作
- 子线程返回请求的接口,有刷新一下UI,此时问题就出现了????
子线程使用的是主线程中的数据拷贝,再删除一行数据之前进行的操作.
我们删除的数据有重新出现了.怎么解决数据源同步问题呢?

(数据源同步2)我们可以在主线程中记录删除操作,我们在子线程即将刷新
UI时,进行同步删除操作,在主线程刷新UI就能保证数据源同步

数据源同步3.png
数据源同步解决方案
2.串行访问

案例:主线程与子线程,串行队列(数据源同步3)
- 子线程进行网络请求,数据解析等操作
- 请求的网络数据,在串行队列中进行数据预排版
- 在主线程删除某一行数据,此时子线程预排版结束后,删除操作需要等待
- 在串行队列中,完成上一个任务,在主线程发来的任务中,同步数据删除
- 回到主线程更新UI,保证数据源同步.

2.事件传递/视图响应

2.1 UIView 和 CALayer
UIView提供内容,负责处理触摸等事件,参与响应链
CALayer负责显示内容
2.2 事件传递
//点击了那个视图
-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;
//点击位置是否在当前视图内
-(BOOL)pointInside:(CGPoint)point wihtEvent:(UIEvent*)event;

2.3 事件传递流程

事件传递流程.png
点击屏幕-->>UIApplication-->>UIWindow-->>最终响应的视图-->>
判断点击的视图是否在UIWindow范围内-->>视图遍历(最后添加的视图
优先遍历)-->>返回的view1如果存在就结束了
视图遍历流程.png
- 开始遍历
- 视图没有隐藏/视图存在点击事件/透明度不为0,视图存在,继续进行,否则返回nil
- 判断点击视图是否在当前视图内,视图存在,继续下一级进行,否则返回nil
- 遍历视图,通过响应视图hitTest方法,判断视图是否存在,如果不存在,继
续遍历,直到找到最后的响应视图.
- 遍历子视图,如果视图不存在,结果为上一层视图v
-(void)touchesBegan:(NSSet*)touches withEvent:(UIEvent *)event;
-(void)touchesMoved:(NSSet*)touchues withEvent:(UIEvent*)event;
-(void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event;
UIResponder的响应方法,UIView继承UIResponder,所以UIView也有这三个方法
面试题:如果传递到UIApplicationDelegate,最终仍然没有任何视图去处理事件,最终是什么样的场景?
忽略这个事件,当做没有发生.

3.图像显示原理

- CPU,GPU两个硬件都是通过总线连接起来的
- 在CPU中输出的结果,是一个位图.
- 通过总线,在合适的时机,在上传给GPU
- GPU拿到这个位图后,会做一些图层的渲染,纹理的合成,最终会把结果帧缓冲器当中(Frame Buffer)
- 随后视频控制器会按照vsync信号逐行读取帧缓冲区的数据,最后显示到我们的显示器上.
图像显示原理2.png
- 我们创建的UIView,是通过CALayer负责的
- CALayer中有个contents属性,绘制屏幕上的位图
- 比如Hello world ,通过drawRect绘制位图
- 绘制的位图,通过Core Anmation框架,提交GPU(OpenGL渲染管线),位图
的渲染及纹理的合成,就会显示在屏幕上面.
CPU工作.png
- CPU工作:布局,显示,准备,提交.
- Layout:UI布局,文本计算
- Display:绘制
- Prepare:图片编解码
- Commit:提交位图
GPU渲染管线.png
补充:
- GPU渲染管线:顶点着色,图元装配,光栅化,片段着色,片段处理
- 经过以上5点:提交到帧缓存区当中,通过视频控制器,显示到我们的显示器

4.UI卡顿,掉帧的原因

UI卡顿,掉帧1.png
- 页面的滑动流畅性是60FPS
- 每一秒,会有60帧画面更新,人眼看到的就是流畅的.
UI卡顿,掉帧2.png
在规定的16.7ms内,在下一帧VSync到来前,CPU和GPU并没有完成下一帧画面的合成,导致卡顿和掉帧.
- CPU:
- 对象创建,调整,销毁可以放到子线程中,节省CPU的时间
- 预排版(布局,文本计算),放到子线程中,主线程响应用户的交互
- 预渲染(文本等异步绘制,图片编解码等)
- GPU:
- 纹理渲染
- 视图混合:减轻视图层级的复杂性,减轻GPU的压力

5.绘制原理

绘制原理.png
[UIView setNeedsDisPlay] 
|
[view.layer setNeedsDisplay]
|
[CALayer display]

- 如果不响应displayLayer方法,就会进入到系统绘制流程当中
- 如果响应displayLayer方法,就开启了异步绘制
- 使用layer的代理方法
- 如果使用,调用系统的drawlayer方法,进行视图绘制
- 如果不使用,调用drawinContext方法
- 最终是对CALayer上传位图到GPU
- [layer.delegate displayLayer:]
- 代理负责生成对应的位图
- 位图作为layer.contents属性的值
异步绘制流程.png
- 在主队列调用setNeedsDisplay
- 在当前runloop将要结束的时候,调用display方法,代理实现displayLayer函数,调用displayLayer方法
- 通过子线程切换,位图绘制
- 子线程中三个方法,CGBitmapContextCreate(),创建位图上下文
- 通过CoreGraphic API进行位图绘制工作
- 通过位图上下文生成图片
- 回到主队列当中,提交位图,设置CALayer的content属性
- 即完成了CALayer的异步绘制.

6.离屏渲染

GPU的渲染操作是在当前用于显示的屏幕缓冲区中进行
GPU在当前屏幕缓冲区以外新开辟一个缓冲区进行渲染操作
离屏渲染何时会触发?
- 圆角(与maskToBounds一起使用时)
- 图层蒙版
- 阴影
- 光栅化
为什么要避免离屏渲染?
- 创建新的渲染缓冲区
- 上下文切换

增加GPU进行额外的开销,导致CPU和GPU合成大于16.7ms,会造成UI卡顿,掉帧

UI视图面试题:

QQ交流群: 796142709

上一篇下一篇

猜你喜欢

热点阅读