RunLoop
来源:互联网 发布:淘宝搬家到微店 编辑:程序博客网 时间:2024/06/05 08:52
1.1 run loops 是一个关联线程的基础设施。run loop是一个事件处理循环,主要用于安排工作和接受事件的。它可以让线程在有工作的时候繁忙,没有工作的时候休眠,如果没有run loop的话,你需要创建while后者for循环来不断监听事件,但这样性能低下。
1.2 每个线程都有一个run loop,你不必手动创建run loop,可以通过CFRunLoopGetCurrent()或者[NSRunLoop currentRunLoop]获取当前线程的run loop。主线程默认开启run loop并接受用户事件。当你点击按钮时,此时对应的点击事件会触发并把事件分配到run loop并处理事件对应的handler。
1.3 run loop事件分为两种,一种是输入源,一种是定时器源。输入源又分为Custom输入源和Port源,Custom是客户端自己发送信号,而Port源是系统自己发送信号的。定时器源是周期性发送信号的源,发送同步的事件。输入源发送的是异步的事件,通常来自其它线程和其它应用。
1.4 run Loop Mode是一个输入源和定时器源的集合,可以创建自己的model(只需要传递一个字符串)或者使用系统提供的mode,只有run Loop运行在特定的mode下,该mode下的源才会被监视并处理事件。
以下是系统提供的mode.
大多数情况下使用的是Default,也可以是Common modes,这个包括了default,model, eventTracking modes。
1.5 主线程是默认开启run loop的,其他线程需要手动开启runloop,所以在使用NSTimer在其他线程时,需要添加到当前线程的runloop
- (void)myThread2 { NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(fireTimer:) userInfo:nil repeats:YES]; NSRunLoop *runloop = [NSRunLoop currentRunLoop]; [runloop addTimer:timer forMode:NSDefaultRunLoopMode]; [runloop run];}
1.6 run loop会自动创建自动释放池,在每次run loop休眠时释放自动释放池的对象。
1.7 cacoa中的Selector源会自动加入到当前runloop上,当Selector执行完毕后从runloop中移除。在其他线程中需要手动开启runloop否则Selector不会执行。
- (void)myThread3 { [self performSelector:@selector(myMethod) withObject:nil afterDelay:0]; NSRunLoop *runloop = [NSRunLoop currentRunLoop]; [runloop run];}
1.8 run loop的简单应用。
- (void)myThread { NSLog(@"线成开始"); _runloop = CFRunLoopGetCurrent(); [NSRunLoop currentRunLoop]; CFRunLoopSourceContext context = {0, (__bridge void *)(self), NULL, NULL, NULL, NULL, NULL, schedule, cancel, perform}; _source = CFRunLoopSourceCreate(NULL, 0, &context); CFRunLoopAddSource(_runloop, _source, kCFRunLoopDefaultMode); CFRunLoopObserverContext observerContext = {0, (__bridge void *)(self), NULL, NULL, NULL}; _observer = CFRunLoopObserverCreate(NULL, kCFRunLoopAllActivities, true, 0, observeCallback, &observerContext); CFRunLoopAddObserver(_runloop, _observer, kCFRunLoopDefaultMode); BOOL done = NO; do { CFRunLoopRunResult result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 10, false); switch (result) { case kCFRunLoopRunFinished: NSLog(@"kCFRunLoopRunFinished"); case kCFRunLoopRunStopped: NSLog(@"kCFRunLoopRunStopped"); done = YES; break; case kCFRunLoopRunTimedOut: NSLog(@"kCFRunLoopRunTimedOut"); break; case kCFRunLoopRunHandledSource: NSLog(@"kCFRunLoopRunHandledSource"); break; } } while (!done); NSLog(@"线程结束");}
这里CFRunLoopRunInMode(kCFRunLoopDefaultMode, 10, false)的10 代表runloop运行的时间,单位为秒。当明确的停止runloop后者没有源在runloop的时候退出,线程结束。
CFRunLoopSourceContext的最后一个参数是一个c函数指针,用于处理事件的。info指针是自定义的参数,用于传递给注册函数的info参数。CFRunLoopSourceContext的结构如下。
typedef struct { CFIndexversion; void *info; const void *(*retain)(const void *info); void(*release)(const void *info); CFStringRef(*copyDescription)(const void *info); Boolean(*equal)(const void *info1, const void *info2); CFHashCode(*hash)(const void *info); void(*schedule)(void *info, CFRunLoopRef rl, CFRunLoopMode mode); void(*cancel)(void *info, CFRunLoopRef rl, CFRunLoopMode mode); void(*perform)(void *info);} CFRunLoopSourceContext;
以下是注册函数的实现。
void perform(void *info) { ViewController *vc = (__bridge ViewController *)info; NSLog(@"执行开始-info=%@", vc); sleep(1); NSLog(@"执行结束-info=%@", vc);}void schedule(void *info, CFRunLoopRef rl, CFRunLoopMode mode) { ViewController *vc = (__bridge ViewController *)info; NSLog(@"schedule info=%@,mode=%@", vc, mode);}void cancel(void *info, CFRunLoopRef rl, CFRunLoopMode mode) { ViewController *vc = (__bridge ViewController *)info; NSLog(@"cancel info=%@,mode=%@", vc, mode);}
schedule函数是添加源的时候执行,perform函数是事件到来时执行,cancel函数是源失效的时候执行。
向源发送信号并唤醒runloop,触发perform函数。
- (IBAction)clickButton:(id)sender { if (CFRunLoopIsWaiting(_runloop)) { if (CFRunLoopSourceIsValid(_source)) { NSLog(@"发送信号"); CFRunLoopSourceSignal(_source); CFRunLoopWakeUp(_runloop); } else { NSLog(@"CFRunLoopSourceInvalidate"); } } else { NSLog(@"CFRunLoopIsWaiting"); }}
让源失效,触发cancel函数。- (IBAction)clickCancelBtn:(id)sender { if (CFRunLoopIsWaiting(_runloop)) { NSLog(@"取消源"); CFRunLoopSourceInvalidate(_source); }}
手动停止runloop。
- (IBAction)clickStopButton:(id)sender { if (CFRunLoopIsWaiting(_runloop)) { NSLog(@"停止RunLoop"); CFRunLoopStop(_runloop); }}
移除源。
- (IBAction)clickRemoveButton:(id)sender { if (CFRunLoopIsWaiting(_runloop)) { NSLog(@"移除源"); CFRunLoopRemoveSource(_runloop, _source, kCFRunLoopDefaultMode); }}
添加runloop观察者。CFRunLoopObserverContext observerContext = {0, (__bridge void *)(self), NULL, NULL, NULL}; _observer = CFRunLoopObserverCreate(NULL, kCFRunLoopAllActivities, true, 0, observeCallback, &observerContext); CFRunLoopAddObserver(_runloop, _observer, kCFRunLoopDefaultMode);
observeCallBack是c函数指针,用于处理观察事件。info指针是自定义的参数,用于传递给注册函数的info参数。void observeCallback(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info) { switch (activity) { case kCFRunLoopEntry: NSLog(@"kCFRunLoopEntry"); break; case kCFRunLoopBeforeTimers: NSLog(@"kCFRunLoopBeforeTimers"); break; case kCFRunLoopBeforeSources: NSLog(@"kCFRunLoopBeforeSources"); break; case kCFRunLoopBeforeWaiting: NSLog(@"kCFRunLoopBeforeWaiting"); break; case kCFRunLoopAfterWaiting: NSLog(@"kCFRunLoopAfterWaiting"); break; case kCFRunLoopExit: NSLog(@"kCFRunLoopExit"); break; default: break; }}
每次运行run loop,你线程的run loop对会自动处理之前未处理的消息,并通知相关的观察者。具体的顺序如下:
- 通知观察者run loop已经启动
- 通知观察者任何即将要开始的定时器
- 通知观察者任何即将启动的非基于端口的源
- 启动任何准备好的非基于端口的源
- 如果基于端口的源准备好并处于等待状态,立即启动;并进入步骤9。
- 通知观察者线程进入休眠
- 将线程置于休眠直到任一下面的事件发生:
- 某一事件到达基于端口的源
- 定时器启动
- Run loop设置的时间已经超时
- run loop被显式唤醒
- 通知观察者线程将被唤醒。
- 处理未处理的事件
- 如果用户定义的定时器启动,处理定时器事件并重启run loop。进入步骤2
- 如果输入源启动,传递相应的消息
- 如果run loop被显式唤醒而且时间还没超时,重启run loop。进入步骤2
- 通知观察者run loop结束。
2017-10-25 16:03:20.434434+0800 aa[5027:3503969] 发送信号2017-10-25 16:03:20.434853+0800 aa[5027:3504031] kCFRunLoopAfterWaiting2017-10-25 16:03:20.434914+0800 aa[5027:3504031] kCFRunLoopBeforeTimers2017-10-25 16:03:20.434948+0800 aa[5027:3504031] kCFRunLoopBeforeSources2017-10-25 16:03:20.435070+0800 aa[5027:3504031] 执行开始-info=<ViewController: 0x102b0eb90>2017-10-25 16:03:21.436837+0800 aa[5027:3504031] 执行结束-info=<ViewController: 0x102b0eb90>2017-10-25 16:03:21.436988+0800 aa[5027:3504031] kCFRunLoopBeforeTimers2017-10-25 16:03:21.437047+0800 aa[5027:3504031] kCFRunLoopBeforeSources2017-10-25 16:03:21.437096+0800 aa[5027:3504031] kCFRunLoopBeforeWaiting2017-10-25 16:03:22.467261+0800 aa[5027:3503969] 发送信号2017-10-25 16:03:22.467683+0800 aa[5027:3504031] kCFRunLoopAfterWaiting2017-10-25 16:03:22.467715+0800 aa[5027:3504031] kCFRunLoopBeforeTimers2017-10-25 16:03:22.467733+0800 aa[5027:3504031] kCFRunLoopBeforeSources
runLoop的使用环境。
1.需要一个辅助线程来监听事件并处理,但需要消耗较低的性能时。
2.使用自定义源或者端口来和其他线程通讯。
3.使用定时器在线程中。
4.使用任何performSelector...方法。
5.保持线程周期性执行任务。
还有尽量避免手动的停止runloop,应该让runloop在一定的时间内退出或者把所有的源移除后自动退出。
- runloop
- runloop
- runloop
- RunLoop
- RUNLOOP
- RunLoop
- RunLoop
- RunLoop
- runloop
- RunLoop
- runloop
- runloop
- runloop
- runloop
- Runloop
- RunLoop
- RunLoop
- runloop
- 数据库存储
- lecture10,Recurrent Neural Network
- python---的topk算法
- 我该如何进入无人驾驶领域?
- ES6-变量的结构赋值-数值和布尔值
- RunLoop
- Ansible实战之Nginx高可用代理LNMP-wordpress
- 【spring学习笔记五】spring事务管理
- linux下so文件的runtime查找顺序
- 第九周项目2---二叉树遍历的递归算法
- 认证鉴权与API权限控制在微服务架构中的设计与实现(一)
- 复变函数ch4-级数
- SQL语句 增删改字段或外键约束 写法
- ES6-变量的结构赋值-函数参数