iOS面试-UI、事件传递、图像显示、性能优化
参考图层性能
事件处理原理
UIView为CALayer提供内容、负责处理触摸等事件,参与响应链
CALayer虽然不参与响应链 但是有一系列的方法帮助处理事件
-containsPoint: //如果该点在该图层 就返回yes
-hitTest: //接受一个point作为参数 返回的是图层本身或者包含这个坐标点的子节点图层
事件传递过程:
点击屏幕后 UIApplication->UIWindow,通过pointinside方法来判断该方法是否在该视图中
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event;
然后在所有子视图中重复该动作 直到找到点击所在区域视图 然后在反向重复进行
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;
找到能处理此事件视图 如果一直传递到UIApplication都没有被处理掉 就会被忽略
图像显示原理
可以参考GPU渲染文档 渲染
简单版本的渲染流程
- 更新视图树
- CPU计算要显示的内容,在runLoop的beforeWatting和Exit时 将更新的视图树打包发送给独立负责渲染的drawRender进程
- 数据在Render Server后 会被反序列化 而在的图层树 根据图层树使用画家算法 得到最终的渲染树 渲染树信息转换为
OpenGL ES/Metal
发送给GPU进行渲染 - GPU进行渲染和纹理合成
- 将结果放到帧缓冲区
- 视频控制器根据Vsync信号 指定时间去提取帧缓冲区的屏幕显示
- 显示到屏幕上
一般页面为60fps, 即一秒就要更新60帧
滑动优化方案
针对CPU 将以下操作放到子线程
- 对象的创建、调整、销毁 放到子线程
- 将布局计算、文本计算、高度缓存等 放到子线程
- 预渲染(文本的异步绘制、图片解码等)
GPU: 纹理渲染 视图合成
一般遇到性能问题,经常考虑是否发生了一下操作
- 是否收到CPU和GPU的限制
- 是否有不必要的CPU渲染
- 是否有太多的离屏渲染
- 是否有奇怪的图片格式或者尺寸
- 是否有太多图层混合操作
- 是否有昂贵的View效果
- View的层次结构是否合理
UI绘制的原理
- 当调用
[UIView SetNeedsDisplay]
时 并没有立即执行绘制工作,而是执行[View.layer setNeedDisplay]
方法 给当前的layer打上“脏”标记 - RunLoop快要结束时 调用layer的display方法 进入当前视图的真正绘制中
- 在layer的display内部 系统判断layer的delegate是否实现
displayLayer:
的delegate方法。如果实现了就执行异步绘制,否则交给系统的绘制流程
系统绘制流程
- 在layer的内部创建一个backing store,即CGContextRef上下文
- 如果layer有delegate:
执行[layer.delegate drawLayer:incntext]方法(这个方法在系统内部执行),在方法内部调用view的drawReact方法,也就是重写view的drawRect方法 才会被调用 - 如果没有delegate就会调用layer的
drawInContext
方法 可以重写layer的该放大 - 将绘制结果提交给GPU
iOS视图约束
自动约束方法
updateConstraintsIfNedded
触发开始约束更新
当有新的布局时,系统调用此方法确保视图和子视图约束信息的更新,系统会自动调用这方法,我们也可以手动调用此方法,来检查最新的约束条件updateConstraints
更新约束
我们可以通过重写此方法来设置自己的约束,系统在调用layOut布局之前,回调用这个方法,确认在视图的约束
当我们的视图有约束变化或者失效,我们可以改变这些约束然后调用setNeedUpdateConstraints
标记约束需要更新
需要在此方法最后调用super updateConstraints
-(BOOL)neddUpdateConstrains
调用此方法根据此方法的返回 决定是否需要更新约束setNeedsUpdateConstraints
当视图需要更新时 我们可以调用此方法,然后在系统决定更新时机
updateConstranins和layOut方法联系
AutoLayout在更新显示view之前,会先调用updateContrains和layingOutView,layout和依赖于Contrains,而view的最终显示依赖于layout
第一步:update constrains 进行视图的约束,从俯视图到子视图更新约束信息,可以通过调用setNeedsUpdateConstraints
来触发此步骤. 而当我们做出一些改变约束的事情事,可以调用updateConstraintsIfNedded
来通知约束改变,可以在updateConstraints
方法中重写或者补充一些视图的约束
第二步: lauout 根据上一步的约束结果,从父视图到子视图来设置视图的bounds frame等 确定试图位置,与constrains类似 有setNeedLayout
layoutIfNeeded
layoutSubviews
方法
第三步: 此步骤时 把view展示在屏幕上 此步骤可以通过setNeedDisplay
来触发
这三个步骤互相依赖,每一步的触发都依赖于上一步
补充
`UIViewControlle
```r`中有`- (void)updateViewConstraints`默认实现是调用UIView的`uodateConstraints`