iOS|iOS 面试全方位剖析 -- UI视图篇(一)

  • 系统的UI事件传递机制是怎么样的 ?
  • 使UITableView滚动更流畅的方案或思路都有哪些 ?
  • 什么是离屏渲染 ?
  • UIView和CALayer之间的关系是怎样的 ?
    如果不是很清楚,请往下看
会讲解的内容
  • UITableView相关
  • 事件传递&视图响应 (一)
  • 图像显示原理
  • 卡顿&掉帧
  • 绘制原理&异步绘制
  • 离屏渲染 (二)
UITableView相关
  • 重用机制
  • 数据源同步
重用机制
iOS|iOS 面试全方位剖析 -- UI视图篇(一)
文章图片
这里A1到A7视为同一个标识符,虚线是可视区域,当A1滑出可视区域的时候会放入重用池A,A7根据标识符从备用池取出一个可重用的cell,这样就达到了重用的一个目的
数据源同步
如何解决tableView在多线程的情况下修改或者访问数据源的一个同步问题?
-- 俩个解决方案
  • 并发访问 & 数据拷贝
  • 串行访问
并发访问 & 数据拷贝 iOS|iOS 面试全方位剖析 -- UI视图篇(一)
文章图片
上图中,一般做数据拷贝在主线程当中,拷贝之后会把拷贝的结果给子线程使用,同时在子线程中做新数据的网络请求,数据解析。
主线程在子线程请求数据的时候删除了一条数据,然后reloadUI
子线程在完成一系列操作之后,返回请求的结果,然后reloadUI。
这个时候问题就出现了,子线程的拷贝发生在主线程删除一条数据之前,所以子线程在返回给主线程的数据源列表中还包括了主线程删除的那条数据,导致数据异常.
怎么解决这种数据源的同步问题呢? 往下看
iOS|iOS 面试全方位剖析 -- UI视图篇(一)
文章图片
我们可以在主线程进行删除操作的时候把它记录下来,在子线程中将要返回数据更新主线程UI的时候同步删除操作。这就是并发访问的解决方案
串行队列 串行访问,这个时候要用到GCD中的串行队列
iOS|iOS 面试全方位剖析 -- UI视图篇(一)
文章图片
子线程做网络请求,数据解析后数据放到串行队列中做预排版(都是在子线程当中完成). 在这个过程中如果在主线程中删除了一条数据,需要以同步的方式在串行队列中处理, 在上个任务数据排版完成之后再同步数据删除,然后再回到主线程更新UI,这样可以保证无论在主线程或者子线程都是在串行队列上进行操作的,避免数据源的错乱问题。
俩种方案各有利弊,具体视项目实际业务而定。
这里多提一下,队列和线程的关系,在iOS 开发中除了一个特殊情况(主队列和主线程的关系),它二者之间是相互独立的,其实只要记住一个要点就可以了,主队列是一个特殊的队列,首先,它是一个串行队列,其次,它自始至终占用一个特殊的线程--主线程,而主线程,自始至终仅被主队列占用,其他任何队列,不管并行还是串行,都不能占用主线程,除去这个奇葩之外,任意队列不管是系统的还是自建的,不管是串行还是并行的,所有跑在这些队列上的线程都是子线程,都不能用来刷新UI
事件传递&视图响应 CALayer 与 UIView 关系
UIView 为CALayer提供内容,专门负责处理触摸等时间,参与响应链
CALayer 全权负责显示内容 contents
这里用到了六大设计原则之中的单一职责原则,这就是为什么要区分UIView 和 CALayer 之间的工作分工.
事件传递机制
基本上是一个必考点,事件传递与俩个方法有关
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event //返回最终响应的事件 - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event //判断点击位置是否在当前范围内

在看事件传递机制之前,先看一下 hitTest 的内部方法实现,看下图

iOS|iOS 面试全方位剖析 -- UI视图篇(一)
文章图片
  1. 首先判断当前视图 !hidden &$ userInteractionEnable && alpha > 0.01 条件通过的时候,到下一步. 否则返回nil,找不到当前视图
  2. 通过 pointInside 判断点击的点是否在当前范围内,为YES直接下一步. 不在则直接返回nil。
  3. 倒序遍历所有子视图,同时调用 hitTest 方法,如果某一个子视图返回了对应的响应视图,这个子视图会直接作为最终的响应视图给响应方,如果为 nil 则继续遍历下一个子视图。如果全部遍历结束都返回nil,那会返回当前点击位置在当前的视图范围内的视图作为最终响应视图.......
当我们点击屏幕时候的事件传递
UIApplication -> UIWindow -> hitTest:withEvent:

深入了解,做一个Demo,一个方形按钮,控制只在点击圆内的时候才响应点击事件,源码看这里(PPEventDemo)
视图响应链(注意和事件传递是倆概念)


看一下官方的一张图,一目了然 iOS|iOS 面试全方位剖析 -- UI视图篇(一)
文章图片
UIResponder 主要有三个方法
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event -(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event -(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event

提问,下图中点击空白圆点,视图响应顺序是什么?如果最后传递到UIApplicationDelegate 依然没有响应,会发生什么?
iOS|iOS 面试全方位剖析 -- UI视图篇(一)
文章图片
【iOS|iOS 面试全方位剖析 -- UI视图篇(一)】正确顺序是 C2 -> B2 -> A
到 UIApplicationDelegate 依然没有任何视图处理该事件的时候,直接忽略此事件, 并不会崩溃。

    推荐阅读