以下内容来自作者:涂耀辉
文章链接: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");
}
网友评论