专栏名称: Nil_Lu
iOS开发工程师
目录
相关文章推荐
中国航务周刊  ·  广州港+东方海外,“株洲-广州”海铁联运班列 ... ·  3 天前  
中国航务周刊  ·  这家货代巨头,与大货主成立合资公司 ·  3 天前  
中国航务周刊  ·  浙江海港、福建港口与莆田市政府三方签约 ·  4 天前  
51好读  ›  专栏  ›  Nil_Lu

手绘图解:一次点击事件的面试题(基于RunLoop)

Nil_Lu  · 掘金  ·  · 2017-12-25 09:41

正文

问题Fix:

结合 RunLoop 和实际堆栈信息解释点击事件的传播(与99%的人认为的过程不同)。最终结果在最后的 堆栈信息图 和手绘的 事件完整传递图 中。

事情经过:被某大佬问了个问题:描述下Button的点击事件

像我这种小白开发一般都是从事件的传递来讲的:就是UIApplication找寻最优响应者的过程(这里就不赘述了)。

好吧,直接给出总结的答案:

首先,需要具备RunLoop知识,如果RunLoop快忘光了不建议直接阅读请点击:

YY作者ibireme的博客

前导图【Darwin内核架构图,引自ibireme博客】:

Darwin内核架构图

简短描述: IOKit负责响应硬件事件,Darwin内核发出Source1 <mach_port> 消息。

网上大多数的RunLoop基本上都是抄自这个。确实讲的很好,我也读过这个blog,但是感觉根据blog里对点击事件的讲解理解还是有点抽象。

如果对RunLoop比较了解,Continue:

ibreime的原文中对Source0和Source1的描述如下:

1,Source0 只包含了一个回调(函数指针),它并不能主动触发事件。使用时,你需要先调用 CFRunLoopSourceSignal(source),将这个 Source 标记为待处理,然后手动调用 CFRunLoopWakeUp(runloop) 来唤醒 RunLoop。

2,Source1 包含了一个 mach_port 和一个回调(函数指针),被用于通过内核和其他线程相互发送消息。这种 Source 能主动唤醒 RunLoop 的线程。

下面结合一个最简单的点击事件分析下:

事件描述:点击屏幕,在 - touchesBegan 处打断点
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event

预想过程:

Source1和Source0都可以唤醒RunLoop,所以应该是RunLoop收到Source1直接封装成UIEvent再分发,但是实际发现,RunLoop的堆栈调用信息中并没有Source1的身影,只有Source0?

于是,我决定画个图配合堆栈信息讲述下点击事件的全过程,也帮助各位联系RunLoop的知识。

解释:

堆栈调用信息:

堆栈调用信息

按照RunLoop的说法,这里应该是Source1唤醒RunLoop才对,但是堆栈信息中却没有收到Source1信息,只有Source0(UIEvent属于Source0),

结合ibireme博客对事件响应的描述:

当一个硬件事件(触摸/锁屏/摇晃等)发生后,首先由 IOKit.framework 生成一个 IOHIDEvent 事件并由 SpringBoard 接收。这个过程的详细情况可以参考这里。SpringBoard 只接收按键(锁屏/静音等),触摸,加速,接近传感器等几种 Event,随后用 mach port 转发给需要的App进程。随后苹果注册的那个 Source1 就会触发回调,并调用 _UIApplicationHandleEventQueue() 进行应用内部的分发。 _UIApplicationHandleEventQueue() 会把 IOHIDEvent 处理并包装成 UIEvent 进行处理或分发,其中包括识别 UIGesture/处理屏幕旋转/发送给 UIWindow 等。通常事件比如 UIButton 点击、touchesBegin/Move/End/Cancel 事件都是在这个回调中完成的。







请到「今天看啥」查看全文