您的位置:首页 > 运维架构

Run Loop 详解1

2015-04-08 21:33 225 查看

iOS RunLoop 初识

今天突然才之间才意识到NSTimer这样的运行方式,是在多线程中实现的循环还是在主线程中去实现的呢。当然不可能是在主线程中的while那么简单,那样什么都干不了,简单看了下NSTimer是以同步方式运行的。时间到了,消息发出后,ontimer的函数是在主线程上调用的。

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

- (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],显得代码不紧凑而且容易出错。

 

        Run loop就像它的名字一样,是你thread中的一个循环并对收到的事件进行处理。你的代码提供控制语句用来对run loop进行执行——换句话说:你的代码提供while或for循环来驱动run loop。在你的循环中,你使用run loop对象来“运行”事件处理代码。事件处理代码主要进行接收事件并调用事件处理函数。

        Run loop从两个不同的事件源中接收消息。Input sources(CFRunLoopSource)投递异步消息,通常来自于另一个thread或另一个应用程序。Timer sources(CFRunLoopTimer)当在计划的时间或重复的时间间隔内投递同步消息。两种事件源都使用应用程序指定的处理方式对到达的事件进行处理。下图展示了run loop和不同的事件源结构。


如果我们要写多线程的程序,可能就需要自己来管理Run Loop。

下面说一下楼主提出的方法中的参数:

RunMode: NSDefaultRunLoopMode,可以把这个理解为一个”过滤器“,我们可以只对自己关心的事件进行监视。一般NSDefaultRunLoopMode是最常用的。

启动run loop的方法就是lz写的这个,它的说明如下:

Runs the loop once, blocking for input in the specified mode until a given date.

启动run loop一次,在特定的run loop mode下等待输入。

如果没有附加input source或是timer,或是过limitDate,run loop就会退出,并且方法返回NO。

下来是Run Loop的使用场合:

1. 使用port或是自定义的input source来和其他线程进行通信

2. 在线程(非主线程)中使用timer

3. 使用 performSelector…系列(如performSelectorOnThread, …)

4. 使用线程执行周期性工作

run loop不需要创建,在线程中只需要调用[NSRunLoop currentRunLoop]就可以得到

假设我们想要等待某个异步方法的回调。比如connection。如果我们的线程中没有启动run loop,是不会有效果的(因为线程已经运行完毕,正常退出了)。

 

 

何时使用 Run Loop
仅当在为你的程序创建辅助线程的时候,你才需要显式运行一个 run loop。Run loop 是程序主线程基础设施的关键部分。所以,Cocoa 和 Carbon 程序提供了代码运 行主程序的循环并自动启动 run loop。IOS 程序中 UIApplication 的 run 方法(或 Mac OS X 中的 NSApplication)作为程序启动步骤的一部分,它在程序正常启动的时 候就会启动程序的主循环。类似的,RunApplicationEventLoop 函数为 Carbon
程序 启动主循环。如果你使用 xcode 提供的模板创建你的程序,那你永远不需要自己去显 式的调用这些例程。
对于辅助线程,你需要判断一个 run loop 是否是必须的。如果是必须的,那么 你要自己配置并启动它。你不需要在任何情况下都去启动一个线程的 run loop。比 如,你使用线程来处理一个预先定义的长时间运行的任务时,你应该避免启动 run loop。Run loop 在你要和线程有更多的交互时才需要,比如以下情况:
 使用端口或自定义输入源来和其他线程通信  使用线程的定时器  Cocoa 中使用任何 performSelector...的方法  使线程周期性工作
 
如果你决定在程序中使用 run loop,那么它的配置和启动都很简单。和所有线程 编程一样,你需要计划好在辅助线程退出线程的情形。让线程自然退出往往比强制关 闭它更好。关于更多介绍如何配置和退出一个 run loop,参阅”使用 Run Loop 对象” 的介绍。
   上代码:
 

使用runloop阻塞线程的正确写法

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);

    end=YES;

    NSLog(@”end.”);

}

但是这样做,运行时会发现,while循环后执行的语句会在很长时间后才被执行。

那是不是可以这样:

[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];

缩短runloop的休眠时间,看起来解决了上面出现的问题。

不过这样也又问题,runloop对象被经常性的唤醒,这违背了runloop的设计初衷。runloop的作用就是要减少cpu做无谓的空转,cpu可在空闲的时候休眠,以节约电量。

那么怎么做呢?正确的写法是:

-(void)runOnNewThread{

     NSLog(@”run for new thread …”);

    sleep(1);

    [self performSelectorOnMainThread:@selector(setEnd) withObject:nil waitUntilDone:NO];

    NSLog(@”end.”);

}
-(void)setEnd{

    end=YES;

}


见黑体斜体字部分,要将直接设置变量,改为向主线程发送消息,执行方法。问题得到解决。

这里要说一下,造成while循环后语句延缓执行的原因是,runloop未被唤醒。因为,改变变量的值,runloop对象根本不知道。延缓的时长总是不定的,这是因为,有其他事件在某个时点唤醒了主线程,这才结束了while循环。那么,向主线程发送消息,将唤醒runloop,因此问题就解决了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  iOS run loop