1.界面刷新时,在什么时候真正执行刷新,为什么会刷新不及时?
当UIView/CALayer相关的属性发生了变化,或者手动调用了setNeedsLayout/setNeedsDisplay的方法时,UIView/CALayer就会被标记为待处理,放在一个全局的容器里。
苹果注册了一个Observer来监听RunLoop的BeforeWaiting状态和Exit状态。当RunLoop处于这样的状态时,就会去调用一个很长的函数来遍历所有的待处理的UIView/CALayer,然后进行实际的绘制和调整,并刷新界面。
2.项目执行过程中,总是有释放池的创建与销毁,这些是在什么时候发生的?
应用启动后,苹果会在主线程注册两个Observer, 回调都是_wrapRunLoopWithAutoreleasePoolHandler()。
a.第一个Observer 监听的是Enter,其回调会调用 _objc_autoreleasePoolPush()来创建自动释放池,并且Order = -2147483647,优先级最高。所以自动释放池的创建会发生在所有回调之前。
b.第二个Observer监听的是BeforeWaiting和Exit。BeforeWaiting会调用_objc_autoreleasePoolPop() 和 _objc_autoreleasePoolPush() 释放旧的释放池,并创建新的释放池。Exit会调用_objc_autoreleasePoolPop() 来释放自动释放池。Order = 2147483647,优先级最低。所以,销毁释放池发生在所有操作之后。
ps:在主线程执行的代码,通常是写在事件回调,Timer回调内的。而这些回调会被放在RunLoop内的自动释放池AutoreleasePool里。所以不会出现内存泄漏,一般不用再创建AutoreleasePool了。
3.事件响应和手势识别 底层处理是一样的吗?为什么?
手势识别只是事件响应事件的一种。
a.事件响应:
苹果注册了一个 Source1 (基于 mach port 的) 用来接收系统事件,其回调函数为 __IOHIDEventSystemClientQueueCallback()。
当一个硬件事件(触摸/锁屏/摇晃等)发生后,首先由 IOKit.framework 生成一个 IOHIDEvent 事件并由 SpringBoard 接收。SpringBoard 只接收按键(锁屏/静音等),触摸,加速,接近传感器等几种 Event,随后用 mach port 转发给需要的App进程。随后苹果注册的那个 Source1 就会触发回调,并调用 _UIApplicationHandleEventQueue() 进行应用内部的分发。
_UIApplicationHandleEventQueue() 会把 IOHIDEvent 处理并包装成 UIEvent 进行处理或分发,其中包括识别 UIGesture/处理屏幕旋转/发送给 UIWindow 等。通常事件比如 UIButton 点击、touchesBegin/Move/End/Cancel 事件都是在这个回调中完成的。
b.手势识别:
当上面的 _UIApplicationHandleEventQueue() 识别了一个手势时,其首先会调用 Cancel 将当前的 touchesBegin/Move/End 系列回调打断。随后系统将对应的 UIGestureRecognizer 标记为待处理。
苹果注册了一个 Observer 监测 BeforeWaiting (Loop即将进入休眠) 事件,这个Observer的回调函数是 _UIGestureRecognizerUpdateObserver(),其内部会获取所有刚被标记为待处理的 GestureRecognizer,并执行GestureRecognizer的回调。
当有 UIGestureRecognizer 的变化(创建/销毁/状态改变)时,这个回调都会进行相应处理。