4

我试图理解 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
}

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

上面的代码按照我的预期运行。每当doDomeWorkOnBackgroundThread执行方法时,线程从其运行循环中唤醒,执行 while 循环的下一次迭代,检查 BOOL 变量exitThread并找到它的值,因为 false 进入 while 循环并执行 NSlog 语句。类似地,当exitThread:方法执行时,exitThread变量设置为 true,这会导致 while 循环和线程退出。

但是,我需要更多说明:

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

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

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

由于exitBackgroundThread在后台线程上执行 CFRunLoopGetCurrent() 应该给出后台线程的 RunLoop。因此,无论我使用哪种 NSRunLoop 方法来启动 RunLoop,理想情况下都应该停止后台线程的运行循环。所以无论如何线程必须在调用上述函数时退出。但它只是没有发生。

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

**编辑

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

4

2 回答 2

3
  1. 当您使用runorrunUntilDate:而不是时,您看到的更改runMode:beforeDate:是预期的。的文档是runMode:beforeDate:这样说的:

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

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

    另一方面,文档是run这样说的:

    它通过重复调用 runMode:beforeDate: 在 NSDefaultRunLoopMode 中运行接收器。换句话说,此方法有效地启动了一个无限循环,该循环处理来自运行循环的输入源和计时器的数据。

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

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

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

    CFRunLoopStop所以你不能依赖run回报。

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

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

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

    while(!exitThread) {
        NSLog(@"Thread did some work");
        CFRunLoopRun([NSRunLoop currentRunLoop].getCFRunLoop);
    }
    
于 2013-08-15T19:16:53.937 回答
1

说的文件runMode:beforeDate:

运行一次循环,在给定日期之前阻塞指定模式下的输入。

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

但是runUntilDate:,如果您将其编码为:

[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:5.0]];
于 2013-08-18T07:53:45.487 回答