Run Loop

作者: 你猜我猜不猜你猜我猜不猜 | 来源:发表于2019-05-23 08:58 被阅读0次

    以下内容来自作者:涂耀辉
    文章链接:https://www.jianshu.com/p/4d5b6fc33519
    本文只是对该文章的摘要和个人总结

    Run Loop 开启方式

    // NSRunLoop
    - (void)run;
    - (void)runUntilDate:(NSDate *)limitDate;
    - (BOOL)runMode:(NSRunLoopMode)mode beforeDate:(NSDate *)limitDate; 
    
    // CFRunLoop  NSRunLoop基于CFRunLoop封装
    CF_EXPORT void CFRunLoopRun(void);
    /*
    参数
    mode: 在哪种Model下运行
    seconds:持续时间
    returnAfterSourceHandled:处理完Input Source就结束,但是对循环状态的Timer无效
    返回
    CFRunLoopRunResult
    kCFRunLoopRunFinished = 1, //Run Loop结束,没有Timer或者其他Input Source
    kCFRunLoopRunStopped = 2, //Run Loop被停止,使用CFRunLoopStop停止Run Loop
    kCFRunLoopRunTimedOut = 3, //Run Loop超时
    kCFRunLoopRunHandledSource = 4 ////Run Loop处理完事件,注意Timer事件的触发是不会让Run Loop退出返回的,即使CFRunLoopRunInMode的第三个参数是YES也不行
     */
    CF_EXPORT CFRunLoopRunResult CFRunLoopRunInMode(CFRunLoopMode mode, CFTimeInterval seconds, Boolean returnAfterSourceHandled);  
    
    

    Run Loop 停止方式

    1)移除掉runloop中的所有事件源(timer和source)。
    2)设置一个超时时间。
    3)只要CFRunloop运行起来就可以用:void CFRunLoopStop( CFRunLoopRef rl );去停止。
    void CFRunLoopStop( CFRunLoopRef rl ); 还可以停止:- (BOOL)runMode:(NSRunLoopMode)mode beforeDate:(NSDate *)limitDate;

    RunLoop 开启的源码(简化版)

    由于原版源码太长不予贴出
    源码地址
    源码解说地址

    /// 用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);
    }
    

    Run Loop Observer

    Core Foundation层的接口可以定义一个Run Loop的观察者在--- Run Loop进入以下某个状态时得到通知:

    • Run loop的进入
    • Run loop处理一个Timer的时刻
    • Run loop处理一个Input Source的时刻
    • Run loop进入睡眠的时刻
    • Run loop被唤醒的时刻,但在唤醒它的事件被处理之前
    • Run loop的终止
    • Observer的创建以及添加到Run Loop中需要使用Core Foundation的接口:
    // 创建observer
    CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
    
    });
    // 添加观察者:监听RunLoop的状态
    CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);
    // 释放Observer
    CFRelease(observer);
    
    /*
    typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
    kCFRunLoopEntry = (1UL << 0), // 1 // 即将进入Loop
    kCFRunLoopBeforeTimers = (1UL << 1), // 2 // 即将处理 Timer
    kCFRunLoopBeforeSources = (1UL << 2), // 4 即将处理 Source
    kCFRunLoopBeforeWaiting = (1UL << 5), // 32 // 即将进入休眠
    kCFRunLoopAfterWaiting = (1UL << 6), // 64
    // 刚从休眠中唤醒
    kCFRunLoopExit = (1UL << 7), // 128 // 即将退出Loop
    kCFRunLoopAllActivities = 0x0FFFFFFFU // 可以监听以上所有状态
    };
    */
    
    

    示例

    线程保活

    
    - (void)testDemo1
    {
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            NSLog(@"线程开始");
            //获取到当前线程
            self.thread = [NSThread currentThread];
    
            NSRunLoop *runloop = [NSRunLoop currentRunLoop];
            //添加一个Port,同理为了防止runloop没事干直接退出
            [runloop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
    
            //运行一个runloop,[NSDate distantFuture]:很久很久以后才让它失效
    //        [runloop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
    
    
            SInt32 result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 5, YES);
    
    
            NSLog(@"线程结束");
    
        });
    
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            //在我们开启的异步线程调用方法
            [self performSelector:@selector(recieveMsg) onThread:self.thread withObject:nil waitUntilDone:NO];
        });
    }
    
    - (void)recieveMsg
    {
        NSLog(@"收到消息了,在这个线程:%@",[NSThread currentThread]);
    }
    
    

    Timer(主要是看返回值,利用了Timer事件)

    - (void)testDemo2
    {
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
    
            NSLog(@"starting thread.......");
            NSTimer *timer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(doTimerTask1:) userInfo:nil repeats:NO];
            [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
    
            //最后一个参数,是否处理完事件返回,结束runLoop
            SInt32 result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 100, NO);
            /*
             kCFRunLoopRunFinished = 1, //Run Loop结束,没有Timer或者其他Input Source
             kCFRunLoopRunStopped = 2, //Run Loop被停止,使用CFRunLoopStop停止Run Loop
             kCFRunLoopRunTimedOut = 3, //Run Loop超时
             kCFRunLoopRunHandledSource = 4 ////Run Loop处理完Source事件,注意Timer事件的触发是不会让Run Loop退出返回的,即使CFRunLoopRunInMode的第三个参数是YES也不行
             */
            switch (result) {
                case kCFRunLoopRunFinished:
                    NSLog(@"kCFRunLoopRunFinished");
    
                    break;
                case kCFRunLoopRunStopped:
                    NSLog(@"kCFRunLoopRunStopped");
    
                case kCFRunLoopRunTimedOut:
                    NSLog(@"kCFRunLoopRunTimedOut");
    
                case kCFRunLoopRunHandledSource:
                    NSLog(@"kCFRunLoopRunHandledSource");
                default:
                    break;
            }
    
            NSLog(@"end thread.......");
    
        });
    
    }
    
    - (void)doTimerTask1:(NSTimer *)timer
    {
    
        NSLog(@"do timer task");
    
        count++;
        if (count == 2) {
            [timer invalidate];
        }
        NSLog(@"do timer task count:%d",count);
    }
    
    

    基于端口的输入源的线程通信

    -(void)testDemo3{
        //声明两个端口   随便怎么写创建方法,返回的总是一个NSMachPort实例
        NSMachPort *mainPort = [[NSMachPort alloc]init];
        NSPort *threadPort = [NSMachPort port];
        //设置线程的端口的代理回调为自己
        threadPort.delegate = self;
        //给主线程runloop加一个端口
        [[NSRunLoop currentRunLoop]addPort:mainPort forMode:NSDefaultRunLoopMode];
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            //添加一个Port
            [[NSRunLoop currentRunLoop]addPort:threadPort forMode:NSDefaultRunLoopMode];
            [[NSRunLoop currentRunLoop]runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
    
        });
        NSString *s1 = @"hello";
        NSData *data = [s1 dataUsingEncoding:NSUTF8StringEncoding];
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            // 发送mainPort端口只是为了验证远程端口是不是mainPort
            NSMutableArray *array = [NSMutableArray arrayWithArray:@[mainPort,data]];
            //过2秒向threadPort发送一条消息,第一个参数:发送时间。msgid 消息标识。
            //components,发送消息附带参数。reserved:为头部预留的字节数(从官方文档上看到的,猜测可能是类似请求头的东西...)
            [threadPort sendBeforeDate:[NSDate date] msgid:1000 components:array from:mainPort reserved:0];
        });
    
    
    }
    
    -(void)handlePortMessage:(id)message{
        NSLog(@"收到消息了,线程为:%@",[NSThread currentThread]);
        //只能用KVC的方式取值
        NSArray *array = [message valueForKeyPath:@"components"];
        NSData *data =  array[1];
        NSString *s1 = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
        NSLog(@"%@",s1);
    
    
        NSMachPort *localPort = [message valueForKeyPath:@"localPort"]; // 接收的端口
        NSMachPort *remotePort = [message valueForKeyPath:@"remotePort"]; // 发送的端口  内存地址和array[0](mainPort)相等
    }
    

    在其他线程唤醒线程调用自定义source事件

    -(void)testDemo4{
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            NSLog(@"starting thread.......");
    
            self->_runLoopRef = CFRunLoopGetCurrent();
            //初始化_source_context。
            bzero(&self->_source_context, sizeof(self->_source_context));
            //这里创建了一个基于事件的源,绑定了一个函数
            self->_source_context.perform = fire;
            //参数
            self->_source_context.info = "hello";
            //创建一个source
            self->_source = CFRunLoopSourceCreate(NULL, 0, &self->_source_context);
            //将source添加到当前RunLoop中去
            CFRunLoopAddSource(self->_runLoopRef, self->_source, kCFRunLoopDefaultMode);
    
            //开启runloop 第三个参数设置为YES,执行完事件后返回
    //        CFRunLoopRunInMode(kCFRunLoopDefaultMode, 9999999, YES);
    
            switch (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 9999999, YES)) {
                case kCFRunLoopRunFinished:
                    NSLog(@"kCFRunLoopRunFinished");
                    break;
                case kCFRunLoopRunStopped:
                    NSLog(@"kCFRunLoopRunStopped");
                    break;
                case kCFRunLoopRunTimedOut:
                    NSLog(@"kCFRunLoopRunTimedOut");
                    break;
                case kCFRunLoopRunHandledSource:
                    NSLog(@"kCFRunLoopRunHandledSource");
                    break;
                default:
                    break;
            }
    
    
            NSLog(@"end thread.......");
    
        });
    
    
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    
            if (CFRunLoopIsWaiting(self->_runLoopRef)) {
                NSLog(@"RunLoop 正在等待事件输入");
                //调用就执行source事件,不调用不执行
                CFRunLoopSourceSignal(self->_source);
                //线程唤醒后发现由事件需要处理,于是立即处理事件
                CFRunLoopWakeUp(self->_runLoopRef);
            }else {
                NSLog(@"RunLoop 正在处理事件");
                //添加输入事件,当前正在处理一个事件,当前事件处理完成后,立即处理当前新输入的事件
                CFRunLoopSourceSignal(self->_source);
            }
        });
    
    }
    
    static void fire(void* info){
    
        NSLog(@"我现在正在处理后台任务");
    
        printf("%s",info);
    }
    

    同时加入Source事件和Timer事件

    - (void)testDemo5{
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
        CFRunLoopSourceRef source;
        CFRunLoopSourceContext source_context;
        CFRunLoopTimerRef timer;
        CFRunLoopTimerContext timer_context;
    
        bzero(&source_context, sizeof(source_context));
        source_context.perform = _perform;
        source = CFRunLoopSourceCreate(NULL, 0, &source_context);
        CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopCommonModes);
    
        bzero(&timer_context, sizeof(timer_context));
        timer_context.info = source;
        timer = CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent(), 1, 0, 0,
                                     _timer, &timer_context);
        CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopCommonModes);
    
    //    CFRunLoopRun();
      //调用就执行source事件,不调用不执行
        CFRunLoopSourceSignal(source); 
        CFRunLoopRunInMode(kCFRunLoopDefaultMode, 10, YES);
        NSLog(@"end");
        });
    }
    static void
    _perform(void *info __unused)
    {
        printf("hello11111\n");
    }
    
    static void
    _timer(CFRunLoopTimerRef timer __unused, void *info)
    {
    //    CFRunLoopSourceSignal(info);
        printf("hello22222\n");
    
    }
    

    相关文章

      网友评论

          本文标题:Run Loop

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