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渲染文档 渲染

简单版本的渲染流程

  1. 更新视图树
  2. CPU计算要显示的内容,在runLoop的beforeWatting和Exit时 将更新的视图树打包发送给独立负责渲染的drawRender进程
  3. 数据在Render Server后 会被反序列化 而在的图层树 根据图层树使用画家算法 得到最终的渲染树 渲染树信息转换为OpenGL ES/Metal 发送给GPU进行渲染
  4. GPU进行渲染和纹理合成
  5. 将结果放到帧缓冲区
  6. 视频控制器根据Vsync信号 指定时间去提取帧缓冲区的屏幕显示
  7. 显示到屏幕上

一般页面为60fps, 即一秒就要更新60帧

滑动优化方案

针对CPU 将以下操作放到子线程

  1. 对象的创建、调整、销毁 放到子线程
  2. 将布局计算、文本计算、高度缓存等 放到子线程
  3. 预渲染(文本的异步绘制、图片解码等)

GPU: 纹理渲染 视图合成

一般遇到性能问题,经常考虑是否发生了一下操作

  1. 是否收到CPU和GPU的限制
  2. 是否有不必要的CPU渲染
  3. 是否有太多的离屏渲染
  4. 是否有奇怪的图片格式或者尺寸
  5. 是否有太多图层混合操作
  6. 是否有昂贵的View效果
  7. 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视图约束

自动约束方法

  1. updateConstraintsIfNedded
    触发开始约束更新
    当有新的布局时,系统调用此方法确保视图和子视图约束信息的更新,系统会自动调用这方法,我们也可以手动调用此方法,来检查最新的约束条件

  2. updateConstraints
    更新约束
    我们可以通过重写此方法来设置自己的约束,系统在调用layOut布局之前,回调用这个方法,确认在视图的约束
    当我们的视图有约束变化或者失效,我们可以改变这些约束然后调用setNeedUpdateConstraints标记约束需要更新
    需要在此方法最后调用super updateConstraints

  3. -(BOOL)neddUpdateConstrains
    调用此方法根据此方法的返回 决定是否需要更新约束

  4. 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`