1. 简介
RunLoop从字面上解析,就是一直循环的跑,实际上它也是在一直在跑。通常来说,一个线程执行完一个任务后,线程就会退出销毁。但是我们可以通过RunLoop操作,使该线程常驻,在有任务的时候唤醒线程执行相应的任务,在没有任务执行的时候保存睡眠状态,时刻准备着任务的呼唤。想想为什么一个程序能够随时响应操作事件,原理也是一样的。
在iOS系统中,提供了NSRunLoop
和CFRunLoopRef
两种对象来供我们操作。由于CFRunLoopRef
是CoreFoundation提供的纯C函数的API,所有的这些都是线程安全的,可以被任何线程调用。而NSRunLoop
是基于CFRunLoopRef
封装的提供面向对象的API,这是API不是线程安全的,仅仅在run loop对应的那个线程上操作,如果添加一个输入源或者定时器给非当前线程的run loop会导致崩溃现象发生。
2. 开启RunLoop的时机
- 使用端口或者自定义的输入源和其他线程通信
- 在线程上使用定时器
- 在cocoa应用中使用任意一个performSelector…方法
- 使得线程不被杀死去做周期性任务
3. 与RunLoop相关的类
- CFRunLoopRef RunLoop
- CFRunLoopModeRef RunLoop的模式
- CFRunLoopSourceRef RunLoop的输入源
- CFRunLoopTimerRef 计时器
- CFRunLoopObserverRef 观察者
3.1 CFRunLoopRef
3.1.1 获取RunLoop
//获取当前线程的RunLoop
CFRunLoopRef CFRunLoopGetCurrent(void);
//获取主线程的RunLoop
CFRunLoopRef CFRunLoopGetMain(void);复制代码
3.1.2 启动和停止RunLoop
//无限期的在默认模式下运行RunLoop,直到运行循环停止,或者将所有源和定时器从默认运行循环模式中移除
void CFRunLoopRun(void);
/* 在特定的模式运行RunLoop
*
* mode 运行的模式
* seconds 运行循环的时间
* returnAfterSourceHandled 处理一个源的循环后是否应该退出
*/
CFRunLoopRunResult CFRunLoopRunInMode(CFRunLoopMode mode, CFTimeInterval seconds, Boolean returnAfterSourceHandled);
//唤醒等待的RunLoop,当处于睡眠时,如果没有输入源或者定时器的触发,将一直处于睡眠,倘若运行循环被修改,如添加新的输入源,则需要唤醒RunLoop处理事件
void CFRunLoopWakeUp(CFRunLoopRef rl);
//停止运行RunLoop
void CFRunLoopStop(CFRunLoopRef rl);
//运行循环是否在等待事件
Boolean CFRunLoopIsWaiting(CFRunLoopRef rl);复制代码
3.1.3 输入源的管理
//将输入源添加到RunLoop
void CFRunLoopAddSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFRunLoopMode mode);
/* 是否包含输入源
*
* source 匹配的输入源
* mode 特有的模式下
*/
Boolean CFRunLoopContainsSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFRunLoopMode mode);
//从RunLoop中移除输入源
void CFRunLoopRemoveSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFRunLoopMode mode);复制代码
3.1.4 模式管理
//将一个模式添加到一组运行循环,一旦添加到运行循环模式中,将无法删除
void CFRunLoopAddCommonMode(CFRunLoopRef rl, CFRunLoopMode mode);
//返回一个模式数组
CFArrayRef CFRunLoopCopyAllModes(CFRunLoopRef rl);
//复制当前的模式
CFRunLoopMode CFRunLoopCopyCurrentMode(CFRunLoopRef rl);复制代码
3.1.5 定时器
//将定时器添加到runLoop
void CFRunLoopAddTimer(CFRunLoopRef rl, CFRunLoopTimerRef timer, CFRunLoopMode mode);
//返回定时器下一个触发时间
CFAbsoluteTime CFRunLoopGetNextTimerFireDate(CFRunLoopRef rl, CFRunLoopMode mode);
//移除定时器
void CFRunLoopRemoveTimer(CFRunLoopRef rl, CFRunLoopTimerRef timer, CFRunLoopMode mode);
//是否包含定时器
Boolean CFRunLoopContainsTimer(CFRunLoopRef rl, CFRunLoopTimerRef timer, CFRunLoopMode mode);复制代码
3.1.6 获取typeID
CFTypeID CFRunLoopGetTypeID(void);复制代码
3.2 CFRunLoopModeRef
一个RunLoop包含N个CFRunLoopModeRef
,每个Mode中又包含了N个CFRunLoopSourceRef
、CFRunLoopTimerRef
、CFRunLoopObserverRef
。但是每次调用的时候,只能指定某一个Mode类型,若想切换Mode类型,必须退出RunLoop再创建RunLoop。
在NSRunLoop中,只提供了两种类型的模式
- NSDefaultRunLoopMode
- NSRunLoopCommonModes
3.3 CFRunLoopSourceRef
CFRunLoopSourceRef是产生事件的地方,分为两种:
- Source0 只包含了一个回调,并不能主动触发事件。当使用的时候,需要先调用 CFRunLoopSourceSignal(source),将这个 Source 标记为待处理,然后手动调用 CFRunLoopWakeUp(runloop) 来唤醒 RunLoop,让其处理这个事件
- Source1 包含了一个 mach_port 和一个回调,被用于通过内核和其他线程相互发送消息。这种 Source 能主动唤醒 RunLoop 的线程
3.3.1 输入源信息
/* 创建CFRunLoopSource对象
*
* allocator 分配内存适配器 使用NULL或者kCFAllocatorDefault
* order 处理运行循环的优先级
* context runLoopSource上下文信息
*/
CFRunLoopSourceRef CFRunLoopSourceCreate(CFAllocatorRef allocator, CFIndex order, CFRunLoopSourceContext *context);
/* 获取RunLoopSource上下文信息
*
* source 输入源
* context 上下文信息的指针
*/
void CFRunLoopSourceGetContext(CFRunLoopSourceRef source, CFRunLoopSourceContext *context);
/* 获取RunLoopSource优先级
*
* source 输入源
*/
CFIndex CFRunLoopSourceGetOrder(CFRunLoopSourceRef source);
//返回类型标识符
CFTypeID CFRunLoopSourceGetTypeID(void);
//移除输入源,阻止再次触发
void CFRunLoopSourceInvalidate(CFRunLoopSourceRef source);
//判断输入源是否有效
Boolean CFRunLoopSourceIsValid(CFRunLoopSourceRef source);
//发送输入源信息,并标记为准备启动
void CFRunLoopSourceSignal(CFRunLoopSourceRef source);复制代码
3.3.2 输入源回调
//当从循环中移除输入源时回调
typedef void (*CFRunLoopCancelCallBack) (
void *info, //CFRunLoopSourceContext
CFRunLoopRef rl, //正在移除输入源的RunLoop
CFStringRef mode //循环模式
);
//两个输入源是否相等的回调
typedef Boolean (*CFRunLoopEqualCallBack) (
const void *info1, // CFRunLoopSourceContext or CFRunLoopSourceContext1
const void *info2 // CFRunLoopSourceContext or CFRunLoopSourceContext1
);
//获取source1 输入源的mach端口
typedef mach_port_t (*CFRunLoopGetPortCallBack) (
void *info //CFRunLoopSourceContext1
);
//info对象hash值的回调
typedef CFHashCode (*CFRunLoopHashCallBack) (
const void *info
);
//接收到mach端口信息时回调
typedef void *(*CFRunLoopMachPerformCallBack) (
void *msg, //mach信息
CFIndex size, //信息的大小
CFAllocatorRef allocator, //内存分配
void *info //CFRunLoopSourceContext1
);
//收到RunLoopSource对象需要处理信息时回调
typedef void (*CFRunLoopPerformCallBack) (
void *info //CFRunLoopSourceContext
);
//将输入源添加到RunLoop时回调
typedef void (*CFRunLoopScheduleCallBack) (
void *info, //CFRunLoopSourceContext
CFRunLoopRef rl, //正在循环的RunLoop
CFStringRef mode //模式
);复制代码
3.3.3 数据类型
typedef struct {
CFIndex version;//版本号必须为0
void * info;//指向数据的任意指针
const void *(*retain)(const void *info);//保留info指针时回调,可以为NULL
void (*release)(const void *info);//定义info指针执行是回调,可以为NULL
CFStringRef (*copyDescription)(const void *info);//定义info指针复制的回调,可以为NULL
Boolean (*equal)(const void *info1, const void *info2);//比较的回调,,可以为NULL
CFHashCode (*hash)(const void *info);//定义info指正hash的回调,可以为NULL
void (*schedule)(void *info, CFRunLoopRef rl, CFRunLoopMode mode);//添加输入源时回调,可以为NULL
void (*cancel)(void *info, CFRunLoopRef rl, CFRunLoopMode mode);//移除输入源的回调,可以为NULL
void (*perform)(void *info);//运行循环执行触发时回调
} CFRunLoopSourceContext;复制代码
3.4 CFRunLoopTimerRef
CFRunLoopTimerRef
主要是定时器,将定时器加入RunLoop中,到时间点到的时候,RunLoop唤醒定时器的执行方法
/* 使用块初始化RunLoop对象
*
* allocator 内存适配器,使用NULL或者kCFAllocatorDefault
* fireDate 首次触发的时间
* interval 定时器触发的间隔
* flags 该字段目前被忽略,0
* order 处理的优先级,定时器可忽略,值为0
* block 定时器触发的块
*/
CFRunLoopTimerRef CFRunLoopTimerCreateWithHandler(CFAllocatorRef allocator, CFAbsoluteTime fireDate, CFTimeInterval interval, CFOptionFlags flags, CFIndex order, void (^block)(CFRunLoopTimerRef timer));
/* 创建RunLoopTimer对象
*
* allocator 内存适配器,使用NULL或者kCFAllocatorDefault
* fireDate 首次触发的时间
* interval 定时器触发的间隔
* flags 该字段目前被忽略,0
* order 处理的优先级,定时器可忽略,值为0
* callout 定时器触发时的回调函数
* context 上下文信息,不需要时可为NULL
*/
CFRunLoopTimerRef CFRunLoopTimerCreate(CFAllocatorRef allocator, CFAbsoluteTime fireDate, CFTimeInterval interval, CFOptionFlags flags, CFIndex order, CFRunLoopTimerCallBack callout, CFRunLoopTimerContext *context);
//判断RunLoopTimer是否重复
Boolean CFRunLoopTimerDoesRepeat(CFRunLoopTimerRef timer);
//联系上下文
void CFRunLoopTimerGetContext(CFRunLoopTimerRef timer, CFRunLoopTimerContext *context);
//返回时间间隔
CFTimeInterval CFRunLoopTimerGetInterval(CFRunLoopTimerRef timer);
//下一个触发时间
CFAbsoluteTime CFRunLoopTimerGetNextFireDate(CFRunLoopTimerRef timer);
//返回优先级
CFIndex CFRunLoopTimerGetOrder(CFRunLoopTimerRef timer);
//返回标识符
CFTypeID CFRunLoopTimerGetTypeID(void);
//移除RunLoopTimer,防止再次触发
void CFRunLoopTimerInvalidate(CFRunLoopTimerRef timer);
//判断是否有效
Boolean CFRunLoopTimerIsValid(CFRunLoopTimerRef timer);
//设置下一个启动日期
void CFRunLoopTimerSetNextFireDate(CFRunLoopTimerRef timer, CFAbsoluteTime fireDate);
/*定时器触发时回调
*
* info CFRunLoopTimerContext
*/
typedef void (*CFRunLoopTimerCallBack)(CFRunLoopTimerRef timer, void *info);
typedef struct {
CFIndex version; //版本号,必须为0
void * info; //任意指针
const void *(*retain)(const void *info);//retain指针,可以为NULL
void (*release)(const void *info);//release指针可以为NULL
CFStringRef (*copyDescription)(const void *info); //定义info指针的描述回调,可以为NULL
} CFRunLoopTimerContext;复制代码
3.5 CFRunLoopObserverRef
CFRunLoopObserverRef
是观察者,关注着RunLoop的状态变化,RunLoop主要有六个状态
- kCFRunLoopEntry 即将进入Loop
- kCFRunLoopBeforeTimers 即将处理Timer
- kCFRunLoopBeforeSources 即将处理Source
- kCFRunLoopBeforeWaiting 进入睡眠
- kCFRunLoopAfterWaiting 从睡眠中唤醒
- kCFRunLoopExit 即将退出
- kCFRunLoopAllActivities 以上的组合
3.5.1 函数类型
/* 创建RunLoopObserver观察者
*
* allocator 内存分配器,使用NULL或kCFAllocatorDefault
* activities 活动的类型
* repeats 一个或者多个通过运行循环调用的标志,如果为no,即使在多个阶段被调用也无效
* order 优先级别
* block 运行时调用
*/
CFRunLoopObserverRef CFRunLoopObserverCreateWithHandler(CFAllocatorRef allocator, CFOptionFlags activities, Boolean repeats, CFIndex order, void (^block)(CFRunLoopObserverRef observer, CFRunLoopActivity activity));
/* 创建RunLoopObserver观察者
*
* allocator 内存分配器,使用NULL或kCFAllocatorDefault
* activities 活动的类型
* repeats 一个或者多个通过运行循环调用的标志
* order 优先级别
* callout 运行时回调
* context 联系上下文
*/
CFRunLoopObserverRef CFRunLoopObserverCreate(CFAllocatorRef allocator, CFOptionFlags activities, Boolean repeats, CFIndex order, CFRunLoopObserverCallBack callout, CFRunLoopObserverContext *context);
//观察者是否重复
Boolean CFRunLoopObserverDoesRepeat(CFRunLoopObserverRef observer);
//返回循环的运行阶段
CFOptionFlags CFRunLoopObserverGetActivities(CFRunLoopObserverRef observer);
//联系上下文
void CFRunLoopObserverGetContext(CFRunLoopObserverRef observer, CFRunLoopObserverContext *context);
//获取优先级
CFIndex CFRunLoopObserverGetOrder(CFRunLoopObserverRef observer);
//获取类型标识符
CFTypeID CFRunLoopObserverGetTypeID(void);
//移除观察者
void CFRunLoopObserverInvalidate(CFRunLoopObserverRef observer);
//观察者是否能够触发
Boolean CFRunLoopObserverIsValid(CFRunLoopObserverRef observer);复制代码
3.5.2 回调函数
/* 观察者对象被触发时回调
*
* observer 观察者
* activity 活动的阶段
* info CFRunLoopObserverContext
*/
typedef void (*CFRunLoopObserverCallBack)(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info);复制代码
3.5.3 数据类型
typedef struct {
CFIndex version; //版本号必须为0
void * info; //定义数据的任意指针
const void *(*retain)(const void *info); //retain指针,可以为NULL
void (*release)(const void *info); //release指针,可以为NULL
CFStringRef (*copyDescription)(const void *info); //info指针复制的回调
} CFRunLoopObserverContext;复制代码
3.5.4 示范
- (void)createObserver{
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
CFRunLoopObserverContext context = {0, (__bridge void *)(self), NULL, NULL, NULL};
CFRunLoopObserverRef observer = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, &myRunLoopObserver, &context);
if (observer) {
CFRunLoopRef cfLoop = [runLoop getCFRunLoop];
CFRunLoopAddObserver(cfLoop, observer, kCFRunLoopDefaultMode);
}
}
void myRunLoopObserver(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info){
NSString *threadName = [NSThread currentThread].name;
if (activity & kCFRunLoopEntry) {
NSLog(@"%@进入runloop",threadName);
}else if (activity & kCFRunLoopBeforeTimers){
NSLog(@"%@即将处理Timers",threadName);
}else if (activity & kCFRunLoopBeforeSources){
NSLog(@"%@即将处理Sources",threadName);
}else if (activity & kCFRunLoopBeforeWaiting){
NSLog(@"%@即将处理进入睡眠",threadName);
}else if (activity & kCFRunLoopAfterWaiting){
NSLog(@"%@从睡眠中唤醒",threadName);
}else if (activity & kCFRunLoopExit){
NSLog(@"%@退出runloop",threadName);
}
}复制代码
4. 获取RunLoop
苹果并不允许我们创建RunLoop对象,只能通过api去获取当前的RunLoop对象。
在NSRunLoop
中,提供了两个属性帮助我们读取RunLoop对象
@property (class, readonly, strong) NSRunLoop *currentRunLoop;
@property (class, readonly, strong) NSRunLoop *mainRunLoop;复制代码
在CFRunLoopRef
中,也提供了两个方法获取CFRunLoopRef
CF_EXPORT CFRunLoopRef CFRunLoopGetCurrent(void);
CF_EXPORT CFRunLoopRef CFRunLoopGetMain(void);复制代码
RunLoop和线程时一一对象的关系,线程在创建的时候并不会创建RunLoop对象,只有主动获取线程中的RunLoop对象中,才会去检测线程中是否拥有RunLoop对象,有则返回该对象;如果没有则创建RunLoop并返回对象
注意
:应该避免使用GCD Global队列来创建RunLoop的常驻线程,因为在创建时可能出现全局队列的线程池满了的情况,因此GCD无法派发任务,很有可能造成奔溃。
4. RunLoop的运用
4.1 启动RunLoop的方式
必须给RunLoop添加一个输入源或者一个定时器才能在辅助线程上开启RunLoop,如果一个RunLoop没有任何源来监控,就会立刻退出。
//无条件运行RunLoop将线程放在一个永久的循环中,无法在定制模式下运行RunLoop
- (void)run;
//在limitDate未到达之前,RunLoop会一直保持着运行
- (void)runUntilDate:(NSDate *)limitDate;
//RunLoop会被执行一次,时间达到或者时间处理完毕后,自动退出
- (void)runMode:(NSString *)mode beforeDate:(NSDate *)limitDate;复制代码
上述三个方法中,其实使用上面的两个方式启动保持一直运行,也就是不断的重复调用最后一个方法。
4.2 退出RunLoop的方式
- 移除input sources或者timer
- 添加一个定时源或者一个超时时间
- 强制退出线程
- 通过方法CFRunLoopStop来停止runloop
注意
:CFRunLoopStop() 方法只会结束当前的 runMode:beforeDate: 调用,而不会结束后续的调用。
虽然有四种方式可以退出RunLoop循环,但是官方建议给RunLoop配置一个超时时间或者停止RunLoop来退出RunLoop。不建议使用移除runLoop的输入源和定时器来使RunLoop退出,因为有些系统程序给run loop增加输入源处理必要的事件。此时代码可能没有注意到这些输入源的存在,因而不能完全移除掉这些输入源,致使RunLoop无法退出。
#import "ExitViewController.h"
#import "CustomThread.h"
@interface ExitViewController (){
NSRunLoop *_runLoop;
NSMachPort *_machPort;
__weak CustomThread *_thread;
BOOL _isStopRunLoop;
}
@end
@implementation ExitViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor grayColor];
CustomThread *thread = [[CustomThread alloc] initWithTarget:self selector:@selector(createRunLoop) object:nil];
[thread setName:@"com.donyau.thread"];
[thread start];
_thread = thread;
}
- (void)createRunLoop{
NSLog(@"当前线程%@",[NSThread currentThread]);
_runLoop = [NSRunLoop currentRunLoop];
CFRunLoopObserverContext context = {0, (__bridge void *)(self), NULL, NULL, NULL};
CFRunLoopObserverRef observer = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, &myRunLoopObserver, &context);
if (observer) {
CFRunLoopRef cfLoop = [_runLoop getCFRunLoop];
CFRunLoopAddObserver(cfLoop, observer, kCFRunLoopDefaultMode);
}
[self runLoopStop];
NSLog(@"执行");
}
void myRunLoopObserver(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info){
NSString *threadName = [NSThread currentThread].name;
if (activity & kCFRunLoopEntry) {
NSLog(@"%@进入runloop",threadName);
}else if (activity & kCFRunLoopBeforeTimers){
NSLog(@"%@即将处理Timers",threadName);
}else if (activity & kCFRunLoopBeforeSources){
NSLog(@"%@即将处理Sources",threadName);
}else if (activity & kCFRunLoopBeforeWaiting){
NSLog(@"%@即将处理进入睡眠",threadName);
}else if (activity & kCFRunLoopAfterWaiting){
NSLog(@"%@从睡眠中唤醒",threadName);
}else if (activity & kCFRunLoopExit){
NSLog(@"%@退出runloop",threadName);
}
}
#pragma mark - CFRunLoopStop
- (void)runLoopStop{
_machPort = (NSMachPort *)[NSMachPort port];
[_runLoop addPort:_machPort forMode:NSRunLoopCommonModes];
[self performSelector:@selector(logRunLoopStop:) withObject:@"1" afterDelay:4];
[self performSelector:@selector(logRunLoopStop:) withObject:@"2" afterDelay:8];
while (!_isStopRunLoop && [_runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) {
}
}
- (void)logRunLoopStop:(NSString *)mesg{
NSLog(@"%@--%@",[NSThread currentThread],mesg);
_isStopRunLoop = YES;
CFRunLoopStop(CFRunLoopGetCurrent());
}
@end复制代码
4.3 RunLoop的输入源
4.3.1 自定义输入源
DYCustomSourceThread.h
#import
#import "DYRunLoopSource.h"
@interface DYCustomSourceThread : NSThread
@property (nonatomic, strong) DYRunLoopSource *runLoopSource;
@end复制代码
DYCustomSourceThread.m
#import "DYCustomSourceThread.h"
#import "DYRunLoopSource.h"
@interface DYCustomSourceThread (){
}
@end
@implementation DYCustomSourceThread
void currentRunLoopObserver(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info){
NSString *threadName = [NSThread currentThread].name;
if (activity & kCFRunLoopEntry) {
NSLog(@"%@进入runloop",threadName);
}else if (activity & kCFRunLoopBeforeTimers){
NSLog(@"%@即将处理Timers",threadName);
}else if (activity & kCFRunLoopBeforeSources){
NSLog(@"%@即将处理Sources",threadName);
}else if (activity & kCFRunLoopBeforeWaiting){
NSLog(@"%@即将处理进入睡眠",threadName);
}else if (activity & kCFRunLoopAfterWaiting){
NSLog(@"%@从睡眠中唤醒",threadName);
}else if (activity & kCFRunLoopExit){
NSLog(@"%@退出runloop",threadName);
}
}
-(void)main{
@autoreleasepool {
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
_runLoopSource = [[DYRunLoopSource alloc] init];
[_runLoopSource addToCurrentRunLoop];
CFRunLoopObserverContext context = {0, (__bridge void*)(self),NULL,NULL,NULL};
CFRunLoopObserverRef observer = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, ¤tRunLoopObserver, &context);
if (observer) {
CFRunLoopRef runLoop = CFRunLoopGetCurrent();
CFRunLoopAddObserver(runLoop, observer, kCFRunLoopDefaultMode);
}
[runLoop run];
}
}
@end复制代码
DYRunLoopSource.h
#import
@interface DYRunLoopSource : NSObject
//将输入源添加到RunLoop
- (void)addToCurrentRunLoop;
//移除输入源
- (void)invalidate;
//唤醒RunLoop
- (void)wakeUpRunLoop;
@end复制代码
DYRunLoopSource.m
#import "DYRunLoopSource.h"
@interface DYRunLoopSource (){
CFRunLoopSourceRef _runLoopSource;
CFRunLoopRef _runLoop;
}
@end
@implementation DYRunLoopSource
#pragma mark 输入源添加进RunLoop时调用
void runLoopSourceScheduleRoutine (void *info, CFRunLoopRef rl, CFRunLoopMode mode){
NSLog(@"RunLoop添加输入源");
}
#pragma mark 将输入源从RunLoop移除时调用
void runLoopSourceCancelRoutine (void *info, CFRunLoopRef runLoopRef, CFStringRef mode){
NSLog(@"移除输入源");
}
#pragma mark 输入源需要处理信息时调用
void runLoopSourcePerformRoutine (void *info){
NSLog(@"输入源正在处理任务");
}
-(instancetype)init{
if (self = [super init]) {
CFRunLoopSourceContext context = {0,(__bridge void *)(self),NULL,NULL,NULL,NULL,NULL,&runLoopSourceScheduleRoutine,&runLoopSourceCancelRoutine,&runLoopSourcePerformRoutine};
_runLoopSource = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);
}
return self;
}
-(void)addToCurrentRunLoop{
_runLoop = CFRunLoopGetCurrent();
CFRunLoopAddSource(_runLoop, _runLoopSource, kCFRunLoopDefaultMode);
}
- (void)invalidate{
CFRunLoopRemoveSource(_runLoop, _runLoopSource, kCFRunLoopDefaultMode);
[self wakeUpRunLoop];
}
-(void)wakeUpRunLoop{
if (CFRunLoopIsWaiting(_runLoop) && CFRunLoopSourceIsValid(_runLoopSource)) {
CFRunLoopSourceSignal(_runLoopSource);
CFRunLoopWakeUp(_runLoop);
}
}
@end复制代码
4.3.2 定时器
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:3 repeats:YES block:^(NSTimer * _Nonnull timer) {
NSLog(@"执行timer事件");
[timer invalidate];
}];
[_runLoop addTimer:timer forMode:NSDefaultRunLoopMode];
[_runLoop run];复制代码
4.3.3 基于端口的输入源
NSMachPort *machPort = (NSMachPort *)[NSMachPort port];
[_runLoop addPort:machPort forMode:NSRunLoopCommonModes];复制代码
demo
参考文章
NSRunLoop的退出方式
避免使用 GCD Global队列创建Runloop常驻线程
深入研究 Runloop 与线程保活
深入理解RunLoop