runLoop

这篇文章主要总结下runloop的主要组成及实现流程,以及衍生出来的一些相关问题:
1.组成
2.主要方法(实现流程)
3.线程保活需要添加item
4.GCD和NSTimer的准确性

1.组成

CFRunLoop类,
CFRunLoopMode类,
CFRunLoopSource类,
CFRunLoopTimer类,
CFRunLoopObserver类

//一个runloop中包含
struct __CFRunLoop {
    pthread_t _pthread;                     //对应的线程,runloop和线程是一一对应的
    CFMutableSetRef _commonModes;           //commonMode是针对kCFRunLoopCommonModes,是_modes的子集
    CFMutableSetRef _commonModeItems;
    CFRunLoopModeRef _currentMode;          //当前的mode
    CFMutableSetRef _modes;                 //runloop中所有的mode
};

//mode
struct __CFRunLoopMode {
    CFMutableSetRef _sources0;      //系统事件
    CFMutableSetRef _sources1;      //线程通信
    CFMutableArrayRef _observers;   //监听者
    CFMutableArrayRef _timers;      //定时器
};

mode类型有

    UITrackingRunLoopMode           //处理滚动
    kCFRunLoopDefaultMode           //默认模式
    UIInitializationRunLoopMode     //启动程序后的过渡mode,启动完成后就不再使用
    GSEventReceiveRunLoopMode       //Graphic相关事件的mode,通常用不到
    kCFRunLoopCommonModes           //监听commonModes中mode,比如mainRunloop中commonModes包含UITrackingRunLoopMode,kCFRunLoopDefaultMode,所以timer设置mode = kCFRunLoopCommonModes,就可以同时监听这两种mode下的活动

2.实现流程

其中runloop主要方法是

SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) {
    
    //判断mode是否存在,mode中是否有需要执行的东西,通过这里可以看出如果需要让一个线程保活,这里需要开启runloop之外,还需要有mode和任务(source,timer,observer...)
    if (NULL == currentMode || __CFRunLoopModeIsEmpty(rl, currentMode, rl->_currentMode)) {
        Boolean did = false;
        if (currentMode) __CFRunLoopModeUnlock(currentMode);
        __CFRunLoopUnlock(rl);
        return did ? kCFRunLoopRunHandledSource : kCFRunLoopRunFinished;
    }
    
    //通知监听者即将进入runloop
    __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
    
    //执行runloop
    result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
    
    //通知监听者已经退出runloop
    __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);

    return result;
}

//就是一个do-while循环
static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {
    
    int32_t retVal = 0;
    do {
        02、通知Observers:即将处理Timers
        03、通知Observers:即将处理Sources
        04、处理Blocks
        05、处理Source0(可能会再次处理Blocks)
        06、如果存在Source1,就跳转到第8步
        07、通知Observers:开始休眠(等待消息唤醒)

            ...(休眠)

        08、通知Observers:结束休眠(被某个消息唤醒)
        01> 处理Timer
        02> 处理GCD Async To Main Queue
        03> 处理Source1
        09、处理Blocks
        10、根据前面的执行结果,决定如何操作
        retVal = ...(来决定是否退出循环)
    } while (0 == retVal);

    return retVal;
}

这个方法run函数中,最终用来执行这些source,timer..的就是下面图中,右边黑框中的函数


WechatIMG19.jpeg

其实我们的函数调用大部分都是始于runLoop,比如ViewController中的viewDidLoad,这些方法(方法链,我指的是整个函数调用栈,什么时候开始的第一个函数调用)什么时候调用,都是runloop来控制的,我们在viewDidLoad中打断点发现


WechatIMG18.jpeg

3.线程保活

-(void)threadAlive {
    NSThread *thread = [[NSThread alloc] initWithBlock:^{
        [[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
        while (1) {
            [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
        }
    }];
    [thread start];
}
  • [[NSRunLoop currentRunLoop] run] 是无法停止的,所以如果不是整个app都需要保活的话要使用上述方案;
  • 需要添加任务到mode中,否则无法开启runloop循环, 上述CFRunLoopRunSpecific函数中有明确的代码;
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容