A run loop for a given thread will wait until one or more of its input sources has some data or event, then fire the appropriate input handler(s) to process each input source that is "ready." After doing so, it will then return to its loop, processing input from various sources, and "sleeping" if there is no work to do. NSRunLoop is better than sleep because it allows the runloop to respond to events while you wait. If you just sleep your thread your app will block even if events arrive.
其实说的简单点儿,NSRunLoop的本质是一个消息机制的处理模式。系统级的RunLoop所示如下
关于 (BOOL) runMode:(NSString *)mode beforeDate:(NSDate *)date这个方法,具体的参数解释如下:
1. mode指定runloop模式来处理输入源。
2. 当date设置为[NSDate distantFuture](将来,基本不会到达的时间),所以除非处理其他输入源结束,否则永不退出处理暂停的当前处理的流程。
一般情况下,当我们使用NSRunLoop的时候,代码都如下所示:
do {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDatedistantFuture]];
} while (!done);
在上面的代码中,参数done为NO的时候,当前runloop会一直接收处理其他输入源,处理输入源之后会再回到runloop中等待其他的输入源;除非done为NO,否则当前流程一直再runloop中,我们用下面的一个实例来详细的解释如何使用NSRunLoop。
- (void) downloadImage:(NSString*)url{
_subThreed = [NSThread currentThread];
NSAutoreleasePool *uploadPool = [[NSAutoreleasePool alloc] init];
done = NO;
characterBuffer = [NSMutableData data];
[[NSURLCache sharedURLCache] removeAllCachedResponses];
NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:[NSURLURLWithString:url]];
connection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (connection != nil) {
do {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDatedistantFuture]];
} while (!done);
}
imageView.image = [UIImage imageWithData:characterBuffer];
// Release resources used only in this thread.
connection = nil;
[uploadPool release];
uploadPool = nil;
_subThreed = nil;
}
上面这段代码特别有意思的地方在于,当实例化一个connection之后,马上执行download图片的代理事件,同时当前进程进NSRunLoop,在这个过程中,可以响应某些输入源,比如操作界面上的控件:点击某个button,滑动等等事件,这些事件都可以顺利执行。直到done为YES的时候,这个NSRunLoop才会结束(在具体的代码中,我们可以在connection的didFailWithError 或者connectionDidFinishLoading代理事件中设置),结束NSRunLoop后, 代码会继续执行下面一行:
imageView.image = [UIImage imageWithData:characterBuffer];
下面是代码在NSRunLoop中处理外部输入源的时候callstack,我们可以清楚的看见这里比较明显的有两个thread,其中之一是NSRunLoop(thread 13),而另外的一个是外部输入源的thread(thread 1)。
所以说到这里,我的理解是上面的NSRunLoop代码和异步网络访问比较类似,不同点在于NSRunLoop在connection结束后会重设循环条件,这样就结束NSRunLoop的运行,然后NSRunLoop后面的代码就继续执行,而异步网络访问则需要在connection的connectionDidFinishLoading里面执行后续的代码。为什么这样做呢,其实道理很简单,为了让整个代码的逻辑更加清楚,如果我们没有这样的一个runloop的话,就不得不在子线程的结束的地方,加上imageView.image = [UIImage imageWithData:characterBuffer];(这个有可能是界面操作),则显得代码不够紧凑,容易出错。