美文网首页面试集锦
RunLoop的事件队列

RunLoop的事件队列

作者: 面糊 | 来源:发表于2016-06-14 23:13 被阅读182次

    RunLoop的事件队列

    1. 每次运行RunLoop, 线程中的RunLoop会自动处理线程中的任务, 并且通知观察者, 汇报当前的状态, 顺序如下

      1. 通知观察者RunLoop已经启动
      2. 通知观察者任何即将要开启的定时器
      3. 通知观察者任何即将要启动的非基于端口的事件源
      4. 启动任何准备好的非基于端口的事件源
      5. 如果基于端口的事件源准备好并处于等待状态, 就立即启动, 并且进入步骤9
      6. 通知观察者线程即将进入休眠
      7. 将线程置于休眠状态, 直至以下事件的发生
        • 某一事件到达基于端口的源事件
        • 定时器启动
        • RunLoop设置的事件已经超时
        • RunLoop被显式唤醒
      8. 通知观察者线程即将被唤醒
      9. 处理事件
        • 如果用户定义的定时器启动, 处理定时器事件并且重启RunLoop, 然后进入步骤2
        • 如果输入源启动, 传递响应的信息
        • 如果RunLoop被现实唤醒, 并且事件还没超时, 重启RunLoop, 进入步骤2
      10. 通知观察者RunLoop结束
    2. 代码解释

       // 用DefaultMode启动
       void CFRunLoopRun(void) {
           CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);
       }
         
       // 用指定的Mode启动,允许设置RunLoop超时时间
       int CFRunLoopRunInMode(CFStringRef modeName, CFTimeInterval seconds, Boolean stopAfterHandle) {
           return CFRunLoopRunSpecific(CFRunLoopGetCurrent(), modeName, seconds, returnAfterSourceHandled);
       }
         
       // RunLoop的实现
       int CFRunLoopRunSpecific(runloop, modeName, seconds, stopAfterHandle) {
            
           // 首先根据modeName找到对应mode
           CFRunLoopModeRef currentMode = __CFRunLoopFindMode(runloop, modeName, false);
           // 如果mode里没有source/timer/observer, 直接返回。
           if (__CFRunLoopModeIsEmpty(currentMode)) return;
            
           // 1. 通知 Observers: RunLoop 即将进入 loop。
           __CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopEntry);
            
           // 内部函数,进入loop
           __CFRunLoopRun(runloop, currentMode, seconds, returnAfterSourceHandled) {
                
               Boolean sourceHandledThisLoop = NO;
               int retVal = 0;
               do {
         
                   // 2. 通知 Observers: RunLoop 即将触发 Timer 回调。
                   __CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeTimers);
                   // 3. 通知 Observers: RunLoop 即将触发 Source0 (非port) 回调。
                   __CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeSources);
                   // 执行被加入的block
                   __CFRunLoopDoBlocks(runloop, currentMode);
                    
                   // 4. RunLoop 触发 Source0 (非port) 回调。
                   sourceHandledThisLoop = __CFRunLoopDoSources0(runloop, currentMode, stopAfterHandle);
                   // 执行被加入的block
                   __CFRunLoopDoBlocks(runloop, currentMode);
         
                   // 5. 如果有 Source1 (基于port) 处于 ready 状态,直接处理这个 Source1 然后跳转去处理消息。
                   if (__Source0DidDispatchPortLastTime) {
                       Boolean hasMsg = __CFRunLoopServiceMachPort(dispatchPort, &msg)
                       if (hasMsg) goto handle_msg;
                   }
                    
                   // 通知 Observers: RunLoop 的线程即将进入休眠(sleep)。
                   if (!sourceHandledThisLoop) {
                       __CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeWaiting);
                   }
                    
                   // 7. 调用 mach_msg 等待接受 mach_port 的消息。线程将进入休眠, 直到被下面某一个事件唤醒。
                   // ? 一个基于 port 的Source 的事件。
                   // ? 一个 Timer 到时间了
                   // ? RunLoop 自身的超时时间到了
                   // ? 被其他什么调用者手动唤醒
                   __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort) {
                       mach_msg(msg, MACH_RCV_MSG, port); // thread wait for receive msg
                   }
         
                   // 8. 通知 Observers: RunLoop 的线程刚刚被唤醒了。
                   __CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopAfterWaiting);
                    
                   // 收到消息,处理消息。
                   handle_msg:
         
                   // 9.1 如果一个 Timer 到时间了,触发这个Timer的回调。
                   if (msg_is_timer) {
                       __CFRunLoopDoTimers(runloop, currentMode, mach_absolute_time())
                   } 
         
                   // 9.2 如果有dispatch到main_queue的block,执行block。
                   else if (msg_is_dispatch) {
                       __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
                   } 
         
                   // 9.3 如果一个 Source1 (基于port) 发出事件了,处理这个事件
                   else {
                       CFRunLoopSourceRef source1 = __CFRunLoopModeFindSourceForMachPort(runloop, currentMode, livePort);
                       sourceHandledThisLoop = __CFRunLoopDoSource1(runloop, currentMode, source1, msg);
                       if (sourceHandledThisLoop) {
                           mach_msg(reply, MACH_SEND_MSG, reply);
                       }
                   }
                    
                   // 执行加入到Loop的block
                   __CFRunLoopDoBlocks(runloop, currentMode);
                    
                   if (sourceHandledThisLoop && stopAfterHandle) {
                       // 进入loop时参数说处理完事件就返回。
                       retVal = kCFRunLoopRunHandledSource;
                   } else if (timeout) {
                       // 超出传入参数标记的超时时间了
                       retVal = kCFRunLoopRunTimedOut;
                   } else if (__CFRunLoopIsStopped(runloop)) {
                       // 被外部调用者强制停止了
                       retVal = kCFRunLoopRunStopped;
                   } else if (__CFRunLoopModeIsEmpty(runloop, currentMode)) {
                       // source/timer/observer一个都没有了
                       retVal = kCFRunLoopRunFinished;
                   }
                    
                   // 如果没超时,mode里没空,loop也没被停止,那继续loop。
               } while (retVal == 0);
           }
            
           // 10. 通知 Observers: RunLoop 即将退出。
           __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);
       }
      
    3. 图解


      322CEF6D-7766-4EE3-9169-40D5F564CDD1.png
    4. RunLoop的一般应用

      • NSTimer和GCD定时器
      • PerformSelector: afterDelay
        • 当调用这个方法的时候, 实际内部会创建一个Timer并且添加到当前的RunLoop中, 如果当前线程没有RunLoop, 这个方法也就会失效
      • 在子线程中开启一个RunLoop, 做为常驻线程
      • 自动释放池
      • 手势识别等等

    相关文章

      网友评论

        本文标题:RunLoop的事件队列

        本文链接:https://www.haomeiwen.com/subject/guiudttx.html