NSRunLoop-需要澄清

时间:2013-08-15 18:58:41

标签: objective-c multithreading nsrunloop

我试图理解RunLoops的概念。我已经阅读了关于RunLoops的Apple开发人员指南,并且在某种程度上我已经理解了RunLoops的概念。为了使我的概念更清晰,我编写了一个非常简单的代码,其中使用了RunLoops。代码可以在下面看到。

    - (void)viewDidLoad
{
    [super viewDidLoad];
    thread = [[NSThread alloc] initWithTarget:self selector:@selector(testMethod) object:nil];
    [thread start];
}

- (void)testMethod {
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];

    NSLog(@"Thread Entered");

    NSMachPort* dummyPort = [[NSMachPort alloc] init];
    [[NSRunLoop currentRunLoop] addPort:dummyPort forMode:NSDefaultRunLoopMode];

    while(!exitThread) {
        NSLog(@"Thread did some work");
        [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
    }
    [[NSRunLoop currentRunLoop]
     removePort:dummyPort
     forMode:NSDefaultRunLoopMode];
    [dummyPort release];

    NSLog(@"Thread Exited");
    [pool drain];
}

- (IBAction)doDomeWorkOnBackgroundThread:(id)sender {
    [self performSelector:@selector(dummyMethod) onThread:thread withObject:nil waitUntilDone:NO];
}

- (IBAction)exitThread:(id)sender {
    [self performSelector:@selector(exitBackgroundThread) onThread:thread withObject:nil waitUntilDone:NO];
}

- (void)exitBackgroundThread {
    exitThread = YES;
}

- (void)dummyMethod {
    //Empty
}

在上面的代码中我创建了一个后台线程,在后台线程上我调用了函数testMethod。在testmethod内部我正在运行一段时间来检查BOOL变量exitThread并使用NSRunLoop的- (BOOL)runMode: beforeDate:方法运行后台线程的RunLoop。 有两个IBAction连接到两个按钮。正如IBActions的名称所示,其中一个是exitThread,另一个是唤醒线程并执行在while循环中编写的NSLog语句。

上面的代码就像我预期的那样运行。每当执行doDomeWorkOnBackgroundThread方法线程从其runloop唤醒时,执行while循环的下一次迭代,检查BOOL变量exitThread并在while循环内找到它为false并执行NSlog声明。 类似地,当执行exitThread:方法时,exitThread变量设置为true,这会导致while循环和线程退出。

但是我需要更多的澄清:

1)如果不是在while循环中使用runMode: beforeDate:,而是使用NSRunLoop的runrunUntilDate:方法,那么在exitThread:方法时,线程永远不会退出执行。 exitBackgroundThread方法在后台线程上调用,但while循环从不执行它的下一次迭代(就像我使用runMode: beforeDate:时那样),因此线程永远不会退出。

2)我尝试将exitBackgroundThread方法更改为

- (void)exitBackgroundThread {
        exitThread = YES;
        CFRunLoopStop(CFRunLoopGetCurrent());
    }

由于exitBackgroundThread在后​​台线程上执行,CFRunLoopGetCurrent()应该给出后台线程的RunLoop。所以这应该理想地停止后台线程的运行循环,无论我使用什么NSRunLoop方法来启动RunLoop。所以在任何情况下线程必须在上述函数的调用时退出。但事实并非如此。

我知道我在这里遗漏了一些东西,我正在做相当多的谷歌搜索来找到这个问题的答案,但似乎无法找到正确的答案。

**编辑

我发现this问题与我的第一个查询非常相似。它在很大程度上清除了我的第一个怀疑。

2 个答案:

答案 0 :(得分:3)

  1. 您希望在使用runrunUntilDate:而非runMode:beforeDate:时看到的更改。 runMode:beforeDate:的文档说明了这一点:

      

    在处理完第一个输入源或达到limitDate后返回。

    有一个输入源负责处理performSelector:...请求。因此,当您发送performSelector:...时,运行循环处理输入源然后返回。

    另一方面,run的文档说明了这一点:

      

    它通过反复调用runMode:beforeDate:来运行NSDefaultRunLoopMode中的接收器。换句话说,这种方法有效地开始了一个无限循环,它处理来自运行循环的输入源和定时器的数据。

    因此,在运行循环处理performSelector:...请求的输入源之后,它会等待另一个输入源准备就绪。它不会回来。由于它不会返回,因此while循环永远不会有机会测试exitThread

  2. 您尝试使用CFRunLoopStop是一个好主意,但不幸的是,run的文档说明了这一点:

      

    如果希望运行循环终止,则不应使用此方法。相反,使用其他一个run方法,并在循环中检查自己的其他任意条件。

    因此,您无法依靠CFRunLoopStoprun返回。

    相反,您可以降低到较低级别并使用CFRunLoopRun来运行运行循环,因为记录了CFRunLoopStop以使该函数返回:

      

    此函数强制rl停止运行并将控制权返回给调用当前运行循环激活的CFRunLoopRunCFRunLoopRunInMode的函数。

    所以试试这个来运行你的运行循环:

    while(!exitThread) {
        NSLog(@"Thread did some work");
        CFRunLoopRun([NSRunLoop currentRunLoop].getCFRunLoop);
    }
    

答案 1 :(得分:1)

runMode:beforeDate:的文件说:

  

运行循环一次,在指定模式下阻止输入,直到a   给定日期。

这意味着它只处理'@selector'输入源一次然后返回,而不像run将继续处理下一个输入源而不返回。

但是,如果您将其编码为:{/ p>,则runUntilDate:将在限制日期后返回

[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:5.0]];