美文网首页
iOS Runloop 流程

iOS Runloop 流程

作者: 孙掌门 | 来源:发表于2020-02-19 09:58 被阅读0次

iOS Runloop 基础


1. 一个 runloop 可以包含有很多个 mode
2. 每个 mode 可以有多个 source0 / source1 / Timer / Observer
3. runloop 启动的时候只能对应一个 mode
4. 切换 runloop 的时候,需要退出当前 runloop ,然后重新选择一个新的。
5. 不同 mode 下的 source0/source1/timer/observer ,互不影响,比如 tableView在滚动时候的mode不影响正常情况下的mode事件。
6. 如果mode里面没有source0/source1/timer/observer,会退出

typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
    kCFRunLoopEntry = (1UL << 0),// 即将进入 runloop
    kCFRunLoopBeforeTimers = (1UL << 1),// 即将处理 timer
    kCFRunLoopBeforeSources = (1UL << 2),// 即将处理 source
    kCFRunLoopBeforeWaiting = (1UL << 5),//即将进入休眠
    kCFRunLoopAfterWaiting = (1UL << 6),// 刚才休眠中唤醒
    kCFRunLoopExit = (1UL << 7),// 即将推出
    kCFRunLoopAllActivities = 0x0FFFFFFFU
};

我们runloop 的运行流程为


1. 通知 observer,进入runloop
2. 通知 observer,即将处理 timer 事件
3. 通知 observer,即将处理 source 事件
4. 处理block,runloop 是可以添加 block 的(CFRunLoopPerformBlock(<#CFRunLoopRef rl#>, <#CFTypeRef mode#>, <#^(void)block#>))
5. 处理 source 0
6. 处理 source1事件,如果存在就处理,跳转到第8
7. 通知 observer 即将休眠
8. 通知 observer 结束休眠(timer 事件,source1 事件等)
9. 然后根据前面的执行结果,决定是否再次循环


1. source0 代表触摸事件的处理


我们在 `-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    NSLog(@"---");
}`

这个方法里面打个断电,然后控制台输入 lldb 命令,`bt`,来打印下函数的调用栈


 bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1
  * frame #0: 0x000000010b9e5dad blogTest`-[ViewController touchesBegan:withEvent:](self=0x00007fa8b9703c30, _cmd="touchesBegan:withEvent:", touches=1 element, event=0x0000600002cecaa0) at ViewController.m:108:5
    frame #1: 0x000000010f7d4863 UIKitCore`forwardTouchMethod + 340
    frame #2: 0x000000010f7d46fe UIKitCore`-[UIResponder touchesBegan:withEvent:] + 49
    frame #3: 0x000000010f7e38de UIKitCore`-[UIWindow _sendTouchesForEvent:] + 1867
    frame #4: 0x000000010f7e54c6 UIKitCore`-[UIWindow sendEvent:] + 4596
    frame #5: 0x000000010f7c053b UIKitCore`-[UIApplication sendEvent:] + 356
    frame #6: 0x000000010f84171a UIKitCore`__dispatchPreprocessedEventFromEventQueue + 6847
    frame #7: 0x000000010f8441e0 UIKitCore`__handleEventQueueInternal + 5980
    frame #8: 0x000000010c410471 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
    frame #9: 0x000000010c41039c CoreFoundation`__CFRunLoopDoSource0 + 76
    frame #10: 0x000000010c40fb74 CoreFoundation`__CFRunLoopDoSources0 + 180
    frame #11: 0x000000010c40a87f CoreFoundation`__CFRunLoopRun + 1263
    frame #12: 0x000000010c40a066 CoreFoundation`CFRunLoopRunSpecific + 438
    frame #13: 0x000000011598dbb0 GraphicsServices`GSEventRunModal + 65
    frame #14: 0x000000010f7a7d4d UIKitCore`UIApplicationMain + 1621
    frame #15: 0x000000010b9e8f44 blogTest`main(argc=1, argv=0x00007ffee421ad60) at main.m:20:12
    frame #16: 0x000000010dd6cc25 libdyld.dylib`start + 1

可以看到中间 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ 是 source0 触发的。

2. source1

source1 是基于port 触发的,比如两个线程之间的通讯,线程可能是有 port,两个线程之间的通讯通过这个port通讯

source1 ,系统事件的捕捉,比如点击屏幕的事件,首先是系统事件的捕捉,source1触发,然后传递给source0。

3. timer 事件

我们平时用的 nstimer 或者 perform方法带timer的那个,都属于timeer事件

4. observer 事件

用于监听 runloop 的状态,比如 UI刷新,在我们的runloop即将睡觉之前灰刷新UI,比如我们设置view背景颜色,当我们obser监听到runloop beforeWaiting 之前灰去刷新UI。

验证

我们来监听一下 runloop

// 创建observer
 CFRunLoopObserverRef observer = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, SCX_CFRunLoopObserverCallBack, NULL);
 // 监听  runloop
    CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopCommonModes);
    CFRelease(observer);
     
     // runloop 监听回调
    void SCX_CFRunLoopObserverCallBack(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info){
    switch (activity) {
        case kCFRunLoopEntry:
        {
            NSLog(@"kCFRunLoopEntry");
        }
            break;
            case kCFRunLoopBeforeTimers:
                   {
                       NSLog(@"kCFRunLoopBeforeTimers");
                   }
                       break;
            case kCFRunLoopBeforeSources:
                   {
                       NSLog(@"kCFRunLoopBeforeSources");
                   }
                       break;
          
            case kCFRunLoopBeforeWaiting:
                   {
                       NSLog(@"kCFRunLoopBeforeWaiting");
                   }
                       break;
            case kCFRunLoopAfterWaiting:
                   {
                       NSLog(@"kCFRunLoopAfterWaiting");
                   }
                       break;
            case kCFRunLoopExit:
                   {
                       NSLog(@"kCFRunLoopExit");
                   }
                       break;
            
        default:
            break;
    }

// 触摸屏幕事件
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    NSLog(@"---");
}

然后我们来测试一下,然后拉啊哎点击下屏幕,看会发生什么事情


2020-02-19 10:21:56.965997+0800 blogTest[6792:159633] kCFRunLoopAfterWaiting
2020-02-19 10:21:56.966182+0800 blogTest[6792:159633] kCFRunLoopBeforeTimers
2020-02-19 10:21:56.966323+0800 blogTest[6792:159633] kCFRunLoopBeforeSources
2020-02-19 10:22:00.950737+0800 blogTest[6792:159633] ---
2020-02-19 10:22:00.951125+0800 blogTest[6792:159633] kCFRunLoopBeforeTimers
2020-02-19 10:22:00.951263+0800 blogTest[6792:159633] kCFRunLoopBeforeSources
2020-02-19 10:22:00.959414+0800 blogTest[6792:159633] kCFRunLoopBeforeTimers
2020-02-19 10:22:00.959583+0800 blogTest[6792:159633] kCFRunLoopBeforeSources
2020-02-19 10:22:00.959727+0800 blogTest[6792:159633] kCFRunLoopBeforeTimers
2020-02-19 10:22:00.959859+0800 blogTest[6792:159633] kCFRunLoopBeforeSources
2020-02-19 10:22:00.959997+0800 blogTest[6792:159633] kCFRunLoopBeforeWaiting


从打印我们可以看出,在打印---之前,打印了 kCFRunLoopBeforeSources,说明开始处理 source 事件,我们的触摸屏幕事件也确实是source事件,

相关文章

网友评论

      本文标题:iOS Runloop 流程

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