NSRunloop

1.runloop的概念
runloop 是一个循环方式,系统给我们提供的一种事件循环机制,保证程序可以持续的接受并处理事件,在事件完成后,使程序处在睡眠状态等待唤醒.

2.runloop 的循环结构:
等待事件的传入->线程处在睡眠状态
事件传入->唤醒线程
执行事件->完成后线程进入睡眠状态

3.runloop 的作用
是程序一直运行,并且接受用户的输入(程序处在睡眠状态)
决定应用程序在何时应该处理那些 Event(runloop 这能传入一个 mode,传入新的 mode 时,强制结束上一个循环,开始新的循环).(mode 是处理事件方式 按照什么顺序执行)
调用解耦合(Message Queue,接收消息的任务虚幻执行,类似异步)
节省 CPU(进入睡眠状态)

4,runloop 的结构

CFRunLoopSource 传入的事件 有系统(source0)的也有我们可以传入的(source1)
CFRunLoopTimer 可以传入一个 timer 定时唤醒 runloop
CFRunlObserver 传入一个观察者 可以观察 runloop的状态是否处于唤醒,完成睡眠状态.
这三个可以添加到 runloop 中而 CFRunLoopMode 是对传入的事件进行排序.
线程和 runloop 是一一对应的,一个线程对应一个 runloop,主线程的 runloop 是默认执行的,子线程的 runloop 需要手动启动,启动方式如下

-(void)run; -(void)runUntilDate:(NSDate*)limitDate; -(BOOL)runMode:(NSString *)mode beforeDate:(NSDate *)limitDate;

系统对CFRunLoopRef 进行封装,我们可以直接操作的 runloop 类是 NSRunloop,当然我们不能直接初始化 NSRunloop,只能通过 currentRunLoop mainRunloop 获取
CFRunLoopMode:事件执行的模式
系统开放可以设置事件模式,一个 runloop 只能存在一个事件模式
系统默认的是执行当前的事件后处理后面传入的事件
NSRunLoopCommonMode 模式 是当有事件传入时强制结束上一次循环,并开始执行新传入的事件的循环

5.runloop 的实现原理

NSRunloop_第1张图片

6.调用堆栈

当 runloop 进行回调时,一般都是通过一个很长的函数调用出去(call out),当你在你的代码中使用断点调试的时候,通常能够嗲用堆栈.
iOS 打印调用堆栈的方法:NSLog(@”%@”,[NSThread callStackSymbols]);
这里最重要的是可以看出我们的崩溃出现的地方最后一个就是我们程序停止的地方

    0   WEBIMAGE                            0x0000000107d6e3fd -[ViewController dddd:] + 61
    1   UIKit                               0x0000000108ba2194 -[UIApplication sendAction:to:from:forEvent:] + 92
    2   UIKit                               0x0000000108d116fc -[UIControl sendAction:to:forEvent:] + 67
    3   UIKit                               0x0000000108d119c8 -[UIControl _sendActionsForEvents:withEvent:] + 311
    4   UIKit                               0x0000000108d10af8 -[UIControl touchesEnded:withEvent:] + 601
    5   UIKit                               0x0000000108c1149b -[UIWindow _sendTouchesForEvent:] + 835
    6   UIKit                               0x0000000108c121d0 -[UIWindow sendEvent:] + 865
    7   UIKit                               0x0000000108bc0b66 -[UIApplication sendEvent:] + 263
    8   UIKit                               0x0000000108b9ad97 _UIApplicationHandleEventQueue + 6844
    9   CoreFoundation                      0x000000010871fa31 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
    10  CoreFoundation                      0x000000010871595c __CFRunLoopDoSources0 + 556
    11  CoreFoundation                      0x0000000108714e13 __CFRunLoopRun + 867
    12  CoreFoundation                      0x0000000108714828 CFRunLoopRunSpecific + 488
    13  GraphicsServices                    0x000000010bfb0ad2 GSEventRunModal + 161
    14  UIKit                               0x0000000108ba0610 UIApplicationMain + 171
    15  WEBIMAGE                            0x0000000107d6ea3f main + 111
    16  libdyld.dylib                       0x000000010af2f92d start + 1
)

7.runloop的应用

AutoreleasePool

第一个 Observer 监视的事件是 Entry(即进入 Loop),其回调内会调用_objc_autoreleasePoolPush()创建自动释放池.

第二个 Observer 监视了两个事件:BeforeWaiting(准备进入休眠)时调用_objc_autoreleasePoolPop()和_objc_autoreleasePoolPush()释放旧的池并创建新的池;
Exti(即将退出 Loop)时调用_objc_autoreleasePoolPop()释放自动释放池.

事件响应

当一个硬件事件(触摸/锁屏/摇晃等)发生后,首先由 IOKit.framework 生成一个 IOHIDEvent 事件并由 SpringBoard 接收

SpringBoard 只接收按键(锁屏/静音等),触摸,加速,接近传感器等几种 Event,随后用mach port 转发个需要的 App 进程,随后苹果注册的那个 Source1就会触发回调,并调用_UIApplicationHandleEventQueue(),进行应用内部的分发.

_UIApplicationHandleEventQueue()会把 IOHIDEvent 处理并包装成 UIEvent 进行处理或分发,其中那个包括识别 UIGesture/处理屏幕旋转/发送给 UIWindow 等.通常事件比如 UIButton 点击,touchesBegin/Move/End/Cancel事件都是在这个回调中完成的.

手势识别

当上面的_UIApplicationHandleEventQueue()识别了一个手势时,其首先会调用 Cancel 将当前的touchesBegin/Move/End系列回调打断.随后系统将对应的UIGestureRecognizer 标记为待处理.苹果注册了一个 Observer 监测 BeforeWaiting(Loop 即将进入睡眠)事件,这个 Observer 的回调函数是_UIGestureRecognizerUpdatbserver(),其内部会获取所有刚被标记为待处理的 UIGestureRecognizer,并执行 GestureRecognizer 的回调.当有UIGestureRecognizer变化这个回调都会进行相应的处理

界面更新

当操作 UI时比如改变了 Frame,更新了 UIVeiw/CALayer 的层次是,或者手动调用了 UIView/CALayer 的 setNeedsLayout/setNeedsDisplay 方法后,这个 UIView/CALayer 就被标记为待处理,并被提交到一个全局的容器中,_ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv().这个函数会遍历所有待处理的 UIVeiw/CALayer以执行实际的绘制和调整,并更新 UI界面.

定时器

NSTimer 其实就是 CFRunLoopTimerRef.一个 NSTimer 注册到 RunLoop 后,runloop 会为期重复的时间点注册好事件.例如10:00,10:10,10:20这几个时间点.runloop 为了节省资源,并不会在非常准确的时间点回调这个 Timer.Timer 有个属性叫做 Tolerrance 标示了当时间点到了以后,允许有多大的误差.如果某个时间点被错过了,例如执行了一个很长的任务,则那个时间点得回调也会跳过去,不会延后执行.

你可能感兴趣的:(NSRunloop)