NSRunLoop概述和原理

来源:互联网 发布:2017年二季度经济数据 编辑:程序博客网 时间:2024/06/05 10:50

首先讲NSRunLoop之前, 说说timer这个东西:

我们把环境都设置在Cocoa中,这里所讲的timer就用NStimer,当然这里的原理适用于其他系统的timer。

因为NSTimer是作为一种timer
resource加入到NSRunLoop中去,在当timer的时间累计到规定时间之后就触发timer的action。从这个过程上看来timer应该是很“准时”的,而且现实情况也是这样的,比如一个规定每1s触发的timer绝大多数情况一般也是1s触发一次。但是timer的这种所谓的“准时”千万不要让你产生这样一种幻觉,“timer可以用来作精确的循环控制,比如用来精准控制动画”。

timer的不精确性主要是表现在:timer有可能delay或者丢失。具体有下面几种情形:


  1. runloop被堵塞了
    。timer被加入到runloop中去,如果这个runloop堵塞了,举个例子说就是处理runloop的某个resource花了10S钟,而你的timer是1s触发一次,那么这个时候因为runloop被这个10s的任务所堵塞住了,就没有可能去处理timer了,于是按照Thread
    Programming Guide原文中的说法就是:“if a timer fires when the run loop
    is in the middle of executing a handler routine, the timer waits
    until the next time through the run loop to invoke its handler
    routine. If the run loop is not running at all, the timer never
    fires.” 所以这种情况下,timer可能被delay甚至是丢失掉。

  2. runloop没有run或者run的model timer不支持。
    加入timer加入的是defaule
    mode,但是这个时候用户在如NSRunLoop概述和原理中第一段代码中,用的是某个用户自己定于的mode在run这个runloop那么timer的计时就没有被累加。之有当runloop的model支持该timer的时候,该timer计时才会累计。

所以timer只是一种非实时控制的,“粗略”地计时的一种工具,在通常我们对实时不太要求的时候timer满足我们的需求,但是如果对实时要求很高,比如游戏中,就得采取一些真正实时的手段来实现了。这里我想起了很早的时候看过的一份有戏代码,其中的动画效果都是由NSTimer来控制的,当时我就石化了,虽然游戏各种动画都还能看,但是明显不是很流畅,而且时快时慢。

结合上面说的,又会过头看看SDL中对timer的实现就实在是太简陋了,不过这种简单的timer系统有的时候反而能够提供很好的实时性。

十二 12




先说一下我自己的理解:

iOS
的应用程序里面,程序启动后会有一个 main thread 开始执行 main()
-> UIApplicationMain() 函数,然后为这个 main thread 设置了一个
NSRunLoop 对象,每个线程只可以关联一个 NSRunLoop 对象的,具体怎么关联的目前还不清楚,可以认为 NSThread
和 NSRunLoop 是一对一的关系。

我个人理解的NSRunLoop就是:可以给 NSRunLoop 设置一堆的 Input Sources 和
Observers,它们之间通过一个叫 “Run Loop Mode”的东西关联起来,实际上就是一个NSString 而已,
对于 main thead 的 RunLoop, 就开始不断的的轮询每一个 input source,看看有没有 event
发生,如果有event,就给相关的observer 发送 notification, 给observer去处理event,
例如当发生的 touch event 的时候。


1.什么是NSRunLoop?

我们会经常看到这样的代码:

1

2

3

4

5

6

7

8

9

10


-
 (IBAction)start:(id)sender

{

pageStillLoading = YES;

[
NSThread detachNewThreadSelector:@selector(loadPageInBackground:)toTarget:self
withObject:nil];

[
progress setHidden:NO];

while
 (pageStillLoading) {

[
NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
beforeDate:[NSDate distantFuture]];

}


[
progress setHidden:YES];

}

这段代码很神奇的,因为他会“暂停”代码运行,而且程序运行不会因为这里有一个while循环而受到影响。在[progress
setHidden:NO]执行之后,整个函数想暂停了一样停在循环里面,等loadPageInBackground里面的操作都完成了以后才让[progress
setHidden:YES]运行。这样做就显得简介,而且逻辑很清晰。如果你不这样做,你就需要在loadPageInBackground里面表示load完成的地方调用[progress
setHidden:YES],显得代码不紧凑而且容易出错。

那么具体什么是NSRunLoop呢?其实NSRunLoop的本质是一个消息机制的处理模式。如果你对vc++编程有一定了解,在windows中,有一系列很重要的函数SendMessage,PostMessage,GetMessage,这些都是有关消息传递处理的API。但是在你进入到Cocoa的编程世界里面,我不知道你是不是走的太快太匆忙而忽视了这个很重要的问题,Cocoa里面就没有提及到任何关于消息处理的API,开发者从来也没有自己去关心过消息的传递过程,好像一切都是那么自然,像大自然一样自然?在Cocoa里面你再也不用去自己定义WM_COMMAD_XXX这样的宏来标识某个消息,也不用在switch-case里面去对特定的消息做特别的处理。难道是Cocoa里面就没有了消息机制?答案是否定的,只是Apple在设计消息处理的时候采用了一个更加高明的模式,那就是RunLoop。


2. NSRunLoop工作原理

接下来看一下NSRunLoop具体的工作原理,首先是官方文档提供的说法,看图:

通过所有的“消息”都被添加到了NSRunLoop中去,而在这里这些消息并分为“input source”和“Timer source”
并在循环中检查是不是有事件需要发生,如果需要那么就调用相应的函数处理。为了更清晰的解释,我们来对比VC++和iOS消息处理过程。

VC++中在一切初始化都完成之后程序就开始这样一个循环了(代码是从户sir mfc程序设计课程的slides中截取):

1

2

3

4

5

6

7

8

9


int
 APIENTRY WinMain(HINSTANCE
hInstance,HINSTANCE
hPrevInstance,LPSTR 
lpCmdLine,int nCmdShow){



while
 (GetMessage(&msg, NULL, 0, 0)){

if
 (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)){

TranslateMessage(&msg);

DispatchMessage(&msg);

}


}


}

可以看到在GetMessage之后就去分发处理消息了,而iOS中main函数中只是调用了UIApplicationMain,那么我们可以介意猜出UIApplicationMain在初始化完成之后就会进入这样一个情形:

1

2

3

4

5

6

7


int
 UIApplicationMain(){



while
(running){

[
NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
beforeDate:[NSDate distantFuture]];

原创粉丝点击