NSRunLoop - 需要澄清

4

我正在尝试理解RunLoops的概念。我已经阅读了苹果开发者指南中关于RunLoops的内容,并在某种程度上理解了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内部,我运行了一个while循环,其中检查BOOL变量exitThread并使用NSRunLoop的 -(BOOL)runMode:beforeDate:方法来运行后台线程的RunLoop。 有两个IBAction连接到两个按钮。正如IBAction的名称所示,其中一个是exitThread,另一个是唤醒线程并执行写在while循环中的NSLog语句。
以上代码按照我的预期运行。每当执行doDomeWorkOnBackgroundThread方法时,线程从其runloop中醒来,执行while循环的下一次迭代,检查BOOL变量exitThread,并在找到它的值为false时进入while循环并执行NSlog语句。同样,当执行exitThread:方法时,将exitThread变量设置为true,这将导致while循环和线程退出。
但是,我需要更多的澄清:
1)如果在while循环中使用runrunUntilDate:方法而不是runMode:beforeDate:,则当执行exitThread:方法时线程永远不会退出。在后台线程上调用exitBackgroundThread方法,但是当我使用runMode:beforeDate:时,while循环不会执行其下一次迭代(就像它所做的那样),因此线程永远不会退出。
2)我尝试将exitBackgroundThread方法更改为
- (void)exitBackgroundThread {
        exitThread = YES;
        CFRunLoopStop(CFRunLoopGetCurrent());
    }

由于 exitBackgroundThread 在后台线程上执行,因此CFRunLoopGetCurrent()应该给出后台线程的RunLoop。所以,这应该理想地停止后台线程的运行循环,无论我使用哪种NSRunLoop方法来启动RunLoop。因此,在任何情况下,调用上述函数时线程必须退出。但事实并非如此。
我知道我在这里遗漏了一些东西,并且我正在进行大量的搜索以找到答案,但似乎找不到正确的答案。
**编辑
我发现这个问题与我的第一个查询非常相似。它在很大程度上解决了我的第一个疑问。
2个回答

3
  1. The change you see when you use run or runUntilDate: instead of runMode:beforeDate: is expected. The documentation for runMode:beforeDate: says this:

    it returns after either the first input source is processed or limitDate is reached.

    There is an input source responsible for handling the performSelector:... requests. So when you send performSelector:..., the run loop processes an input source and then returns.

    On the other hand, the documentation for run says this:

    it runs the receiver in the NSDefaultRunLoopMode by repeatedly invoking runMode:beforeDate:. In other words, this method effectively begins an infinite loop that processes data from the run loop’s input sources and timers.

    So after the run loop processes the input source for your performSelector:... request, it waits for another input source to be ready. It doesn't return. Since it doesn't return, your while loop never gets a chance to test exitThread.

  2. Your attempt to use CFRunLoopStop is a good idea, but unfortunately, the documentation for run says this:

    If you want the run loop to terminate, you shouldn't use this method. Instead, use one of the other run methods and also check other arbitrary conditions of your own, in a loop.

    So you can't rely on CFRunLoopStop to make run return.

    Instead, you can drop to a lower level and use CFRunLoopRun to run the run loop, because CFRunLoopStop is documented to make that function return:

    This function forces rl to stop running and return control to the function that called CFRunLoopRun or CFRunLoopRunInMode for the current run loop activation.

    So try this to run your run loop:

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

非常感谢您提供如此详细的解释。但是,我只在文档中找到了CFRunLoopRun()。不过无论如何,它都起作用了。 - tek3

1

runMode:beforeDate:的文档如下:

以指定模式运行循环,阻塞直到给定日期有输入。

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

但是,如果您将其编写为runUntilDate:,它将在限制日期后返回:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接