RunLoop概念与响应者链
一.RunLoop简介
什么是RunLoop?
RunLoop就是运行循环,在程序运行的过程中循环做一些事情,如果没有RunLoop程序执行完毕就会立刻退出,如果有RunLoop程序会一直运行,并且时刻在等待用户的输入操作。RunLoop在有任务的时候会被唤醒,没有任务的时候会进入休眠状态,节省CPU资源,提高程序性能。
二.RunLoop的开启
当我们打开APP的时候,首先进入的是UIApplicationMain函数,RunLoop的作用是维持程序不退出,说明在UIApplicationMain函数中就启动了RunLoop,每一个进程对应一条主线程,主线程默认开启Runloop。
三.响应者链
当用户在界面发生一个点击手势事件,首先由IOKit.framwork产生一个IOHIDEvent对象,并通过mach port(轻量级的进程间通信的方式) 转发到SpringBoard.app(这个应用程序用来管理IOS的主屏幕),然后通过mach port将事情转发到前台APP进程,然后触发由APP在RunLoop注册的Source1(由Runloop和内核管理,mach port驱动)来处理事件,Source1内部调用 IOHIDEventSystemClientQueueCallBack(),IOHIDEventSystemClientQueueCallBack()内部回调了Source0(处理App内部事件,App自己负责管理(触发),如UIEvent,CFSocket)生成了UIEvent事情,并加入事情处理队列中,UIApplication从事件队列中取出事情进行派发,开始了hit-Testing的过程。
四.如何找到第一响应者?
UIApplication会首先把事件传给UIWindow,UIWindow会使用hitTest:withEvent:方法找到此次触摸事件初始点所在的视图,再到这个视图之后他会调用视图的touchesBegan:withEvent:方法来处理事件。
举个例子:
当用户点击ViewD所在的区域时会进行以下hit-Testing:
1、ViewA 的 pointInside 返回 YES,因为触摸点在其 bounds 内。遍历 ViewA 的两个 subview;
2、ViewB 的 pointInside 返回 NO,因为触摸点不在其 bounds 内,ViewB 的 hitTest 方法返回 nil。而且发生一票否决,在 ViewB 上的所有 subviews 受到牵连将不再进行 hit-Testing 处理。
3、ViewC 的 pointInside 返回 YES,因为触摸点在其 bounds 范围内,ViewC 的 hitTest 方法返回默认处理,也就是return [super hitTest:point withEvent:event];遍历 ViewC 的两个 subview。
4、ViewD的 pointInside 返回 YES,因为触摸点在其 bounds 范围内,且ViewD 没有 subview,因此 hitTest 方法返回其自己。hitTesting view 找到,结束处理。
需要注意的地方:
1、hitTest 方法调用 pointInside 方法;
2、hit-Testing 过程是从 superView 向 subView 逐级传递,也就是从层次树的根节点向叶子节点传递;
3、遇到以下设置时,view 的 pointInside 将返回NO,hitTest 方法返回 nil:
view.isHidden=YES;
view.alpha<=0.01;
view.userInterfaceEnable=NO;
control.enable=NO;(UIControl的属性)
五.事件响应
1、如果 hit-test view 或 first responder 不处理此事件,则将事件传递给其 nextResponder 处理,若有 UIViewController 对象则传递给 UIViewController,传递给其 superView。
2、如果 view 的 viewController 也不处理事件,则 viewController 将事件传递给其管理 view 的 superView。
3、视图层级结构的顶级为 UIWindow 对象,如果 window 仍不处理此事件,传递给 UIApplication.
4、若 UIApplication 对象不处理此事件,则事件被丢弃。