type
status
date
slug
summary
tags
category
icon
password

结论:

App 启动就是一个进程的运行,进程会有一个主线程和相对的 runloop 来保证进程的持续运行
线程执行完当前事件就会释放,runloop 是一个死循环,就保证了主线程不会被释放
runloop 负责监听输入源,如输入设备、网络、周期性或延迟时间、异步回调、屏幕刷新、用户触摸、摇晃手机、来电等
runloop 监听到用户触摸屏幕后,会通知 UIApplication 并生成 UITouch 对象,一根手指就是一个 UITouch 对象,两根手指就是两个,所以在 TouchsBegan 等触摸方法中的参数 touchs 是一个无序的 Set 集合
UIApplication 拿到 touches 后会按响应者链条一层一层的往下查找最合适的响应者,过程如下
UIApplication→KeyWindow→rootViewController→view→Subviews→Subviews
在这个链条上所有元素都是 UIControl 的子类,如 window(继承 UIView)、Controller、view
每一次向下传递,都要通过 hitTest 方法递归往下确定元素自身是否可以响应、下一级是否可以响应,pointInSide 方法确定元素自身是否在响应范围内
如果可以响应就接着往下找
如果下一级的所有元素都不可以响应,自己可以响应就返回自己
如果下一级的所有元素和自己都不可以响应,那么自己就不是响应者,返回 nil
最后 UIApplication 找到响应者后,把事件发送给响应者去调用相应的方法
如果 UIApplication 没有找到响应者(hitTest 方法收到 nil),则丢弃该事件

验证如下:

首先一个简单的 demo
我们 clickButton 方法的 print 上打上断点,然后点击 button,看函数调用栈
notion image
我们点击箭头指向按钮看详细调用栈
notion image
这张图从下(26 star)往上(0)看
补充:
第二步 -[UIApplication sendAction:to:from:forEvent:] ()
sendAction: action 为 button 的 action,也就是 addTarget 时传入的 #selector(clickButton)
to: to 的是 ViewController 实例
from: from 是 hitTest 方法一层一层返回的对象,最合适的响应者,也就是 button
forEvent: 就是点击的这一次 UIEvent 事件
第 2、3 步时,button 的子控件 UIImageView 和 UILabel isUserInteractionEnabled 为 false,故 hitTest 方法返回 nil,UIImageView 和 UILabel 不为响应者并不找他们的子控件(虽然也没有),最终返回的就是 button 了
UIApplication hitTest 方法收到 button 后,转发给 ViewController 类的 buttonClick 方法
App 启动速度怎么做优化与监控当你点击了屏幕发生了什么(内存篇)