Run Loop

2019-05-23  本文已影响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进入以下某个状态时得到通知:

// 创建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");

}
上一篇下一篇

猜你喜欢

热点阅读