RunLoop入门
之前在使用NSTimer时,遇到一个问题,NSTimer启动了,但是只会出现一次定时,之后就不起作用。问了同事,他说需要加在RunLoop中,我没有考虑太多,就听他说的,在外层加了一个Runloop,定时器确实起作用了。近期做另一个任务,发现RunLoop在多线程中用的比较多,所以大概查了下相关资料。
runLoop是线程中的循环,并对收到的事件进行处理,它从两个不同的事件源中接收消息。Input sources(CFRunLoopSource)投递异步消息,通常来源于另一个thread或者另一个应用程序。Timer sources(CFRunLoopTimer)当在计划的时间或重复的时间间隔内投递同步消息。两种事件源都使用应用程序指定的处理方式对到达的事件进行处理。下图展示了run loop和不同的事件源结构:
![](https://img.haomeiwen.com/i149194/bc8deb52859932d2.png)
Run Loop的使用场合:
1. 使用port或是自定义的input source来和其他线程进行通信 2. 在线程(非主线程)中使用timer 3. 使用 performSelector…系列(如performSelectorOnThread, …) 4. 使用线程执行周期性工作
run loop不需要创建,在线程中只需要调用[NSRunLoop currentRunLoop]就可以得到假设我们想要等待某个异步方法的回调。比如connection。如果我们的线程中没有启动run loop,是不会有效果的(因为线程已经运行完毕,正常退出了)。 使用runLoop阻塞线程的正确写法 @implementation ViewController { BOOL end; }
…
– (void)viewDidLoad { [super viewDidLoad]; NSLog(@”start new thread …”); [NSThread detachNewThreadSelector:@selector(runOnNewThread) toTarget:self withObject:nil]; while (!end) { NSLog(@”runloop…”); [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate: [NSDate distantFuture]]; NSLog(@”runloop end.”); } NSLog(@”ok.”); }
-(void)runOnNewThread { NSLog(@”run for new thread …”); sleep(1); [self performSelectorOnMainThread:@selector(setEnd) withObject:nil waitUntilDone:NO]; NSLog(@”end.”); }
-(void)setEnd { end=YES; }
每次运行 Run loop,你线程的 Run loop 对会自动处理之前未处理的消息,并通知相关的观察者。具体的顺序如下:
1. 通知观察者 Run loop 已经启动。
2. 通知观察者任何即将要开始的定时器。
3. 通知观察者任何即将启动的非基于端口的源。
4. 启动任何准备好的非基于端口的源。
5. 如果基于端口的源准备好并处于等待状态,立即启动;并进入步骤 9。
6. 通知观察者线程进入休眠。
7. 将线程置于休眠直到任一下面的事件发生:
某一事件到达基于端口的源;
定时器启动;
Run loop 设置的时间已经超时;
Run loop 被显式唤醒。
8. 通知观察者线程将被唤醒。
9. 处理未处理的事件
如果用户定义的定时器启动,处理定时器事件并重启 Run loop。进入步骤 2。
如果输入源启动,传递相应的消息。
如果 Run loop 被显式唤醒而且时间还没超时,重启 Run loop,进入步骤 2。
10. 通知观察者 Run loop 结束。