iOS RunLoop处理source0详解

140 阅读1分钟
创建source0:
// 创建source0
// _rl和_source作为成员变量保存,后面还要用
_rl = [NSRunLoop.currentRunLoop getCFRunLoop];
CFRunLoopSourceContext ctx = {0, NULL, NULL, NULL, NULL, NULL, NULL, sourceSchedule, sourceCancel, sourcePerform};
_source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &ctx);
CFRunLoopAddSource([NSRunLoop.currentRunLoop getCFRunLoop], _source, kCFRunLoopDefaultMode);

// 创建runloop观察者
CFRunLoopObserverRef obs = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopAllActivities, true, 0, observerCallBack, NULL);
CFRunLoopAddObserver([NSRunLoop.currentRunLoop getCFRunLoop], obs, kCFRunLoopDefaultMode);
CFRelease(obs);

// 子线程的runloop需要主动启动
[NSRunLoop.currentRunLoop run];
各种回调:
// source0加入runloop
void sourceSchedule(void *info, CFRunLoopRef rl, CFRunLoopMode mode) {
    NSLog(@"%s", __func__);
}

// source0被取消
void sourceCancel(void *info, CFRunLoopRef rl, CFRunLoopMode mode) {
    NSLog(@"%s", __func__);
}

// source0被执行
void sourcePerform(void *info) {
    NSLog(@"%s", __func__);
}

// 观察者回调
void observerCallBack(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;
    }
}
控制台打印:

WeChatdc4025b8873e035a67f459b74ade1f87.png

给source0发送消息
CFRunLoopSourceSignal(_source);
CFRunLoopWakeUp(_rl); // source0不会自动唤醒runloop,所以需要主动唤醒
控制台打印:

WeChat23174180c343b9eb22ab63f0079735e1.png

总结:

  • source0确实是在kCFRunLoopBeforeSources这一步被处理的,与官方文档描述一致。
  • 值得注意的是每次执行完source0之后runloop都会退出(kCFRunLoopExit)然后再重新进入(kCFRunLoopEntry)。
  • 其实为什么会退出苹果官方文档有讲到:If no input sources or timers are attached to the run loop, this method exits immediately and returns NO; otherwise, it returns after either the first input source is processed or limitDate is reached.