zoukankan      html  css  js  c++  java
  • iOS RunLoop 初识

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

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

    1. - (IBAction)start:(id)sender  
    2. {  
    3. pageStillLoading = YES;  
    4. [NSThread detachNewThreadSelector:@selector(loadPageInBackground:)toTarget:self withObject:nil];  
    5. [progress setHidden:NO];  
    6. while (pageStillLoading) {  
    7. [NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];  
    8. }  
    9. [progress setHidden:YES];  
    10. }  
    复制代码

    这段代码很神奇的,因为他会“暂停”代码运行,而且程序运行不会因为这里有一个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,因此问题就解决了。

     

  • 相关阅读:
    sklearn库学习笔记1——preprocessing库
    juypter notetbook
    信用卡欺诈
    matplotlib1
    python一行输入多个数
    pandas数据预处理
    pandas基础用法
    numpy简单用法2
    numpy 简单用法
    简单循环
  • 原文地址:https://www.cnblogs.com/DamonTang/p/2807088.html
Copyright © 2011-2022 走看看