通常在程序中我们需要处理各种外部事件,特别是客户端程序,比如处理鼠标点击、屏幕触摸等事件。最简单的形式,我们用一个while循环,去轮询各类事件:
while(e = nextEvent()){
processEvent(e);
}
这个是一个主动的轮询,很简单,但是非常低效。即使在没有任何事件,我们的当前线程也会不断地轮询。
那我们能否这么设计:没有事件的时候让线程休眠,有事的时候唤醒线程进行处理。这个机制是与操作系统相关的,应用进程没有办法实现,因为需要操作系统来唤醒应用进程。在iOS中我们把这个技术叫Runloop,由NSRunloop实现,从名字可以看出来是原来loop的替代者。
1.基本原理
从上图可以看出,启动Runloop后线程会休眠直到时钟、网络等事件出现,然后在Runloop中调用相应的处理函数,处理完成后继续休眠。
2.Runloop Modes
Runloop需要响应所有的input sources发出的event吗?否. 可以通过Runloop Modes来过滤。你可以简单的把这些modes当作event filter或者input source filter. 每一个mode对应一系列input sources,其它sources发出的event,该loop不接处理。
3.Runloop可以嵌套
你可以理解成while循环的嵌套,当前Runloop退出后,会回到上一层Runloop,一个事件如果当前Runloop不接受,那么会留给下一个Runloop处理。
4.Runloop的常用方式
1)创建
//常用方式, 若当前无runloop,会自动创建一个
[[NSRunLoop currentRunLoop]
2)运行
[[NSRunLoop currentRunLoop] run]
如果这个时候没有一个input source,比如timer, port, selector,run方法会失败,直接返回NO,否则会运行直到某个date或者有event发生。
5.示例
在多线程的时候通常需要使用Runloop,比如创建了一个线程用户处理所有的网络请求.
//创建一个线程
[NSThread detachNewThreadSelector:@selector(runThread) toTarget:self withObject:nil];
- (void)runThread{
@autoreleasepool { //为当前线程建立autoreleasepool
//启动一个selector,该source会保证NSRunloop运行起来
[self performSelector:@selector(fakeLoop) withObject:nil afterDelay:24*60*60];
while ([[NSThread currentThread] isCancelled] == NO) {
//每隔自动5s唤醒自己1次,用来清理autorelease pool,释放内存
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:5.0]];
}
}
}
//保持总有1个selector事件在Runloop上,否则没有NSURLConnection执行时, Runloop会很快退出
- (void)fakeLoop{
[self performSelector:@selector(fakeLoop) withObject:nil afterDelay:24*60*60];
}
- (void)executeRequest:(NSURLRequest*)aRequest{
NSURLConnection *aConnection = [[NSURLConnection alloc] initWithRequest:aRequest delegate:self];
[aConnection start];
}
欢迎关注”iOS开发之道”的微博和微信帐号,一起交流学习。
微博:iOS开发之道
微信:ioszhidao