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

问题Fix:

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

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

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

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

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

YY作者ibireme的博客

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

简短描述: 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 事件都是在这个回调中完成的。

详细的过程请看图:(水果机拍的,不要吐槽)

点击事件的完整过程:

至此一次Button的点击事件结束,虽然RunLoop天天说,但是在实际开发中却不常用,但是,其实RunLoop就跟设计模式一样,无处不在,知识串起来之后其实对很多Bug的fix和优化会有很大启发。

End.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值