iphone - 主线程的 NSRunLoop 在辅助线程中被引用

标签 iphone objective-c cocoa cocoa-touch multithreading

我最近正在研究一个示例应用程序,试图让我的头脑完全围绕 NSRunLoop。我编写的示例通过 NSOperation 创建了一个简单的辅助线程。辅助线程执行一些任务,例如处理 NSTimer 以及一些使用 NSStream 的基本流。这两个输入源都需要正确配置的 NSRunLoop 才能执行。

我的问题是这样的。最初我在辅助线程中有一些代码看起来像这样:

NSRunLoop* myRunLoop = [NSRunLoop currentRunLoop];
self.connectTimer = [NSTimer scheduledTimerWithTimeInterval:connectTimeout
                                                     target:self
                                                   selector:@selector(connectionConnectedCheck:)
                                                   userInfo:nil 
                                                    repeats:NO];

[myRunLoop addTimer:self.connectTimer forMode:NSDefaultRunLoopMode]; // added source here
[myRunLoop run];

[NSStream getStreamsToHostNamed:relayHost port:relayPort inputStream:&inputStream outputStream:&outputStream];
if ((inputStream != nil) && (outputStream != nil))
{
    sendState = kSKPSMTPConnecting;
    isSecure = NO;

    [inputStream retain];
    [outputStream retain];

    [inputStream setDelegate:self];
    [outputStream setDelegate:self];

    [inputStream scheduleInRunLoop: myRunLoop //[NSRunLoop currentRunLoop]  
                                    forMode:NSRunLoopCommonModes];
    [outputStream scheduleInRunLoop: myRunLoop //[NSRunLoop currentRunLoop] 
                                    forMode:NSRunLoopCommonModes];

    [inputStream open];
    [outputStream open];

    self.inputString = [NSMutableString string];



    return YES;
}

现在,使用上面的代码,事件永远不会处理。不在 currentRunLoop 上。之后我做了一些糟糕的事情,因为这只是一个教育练习,并将其修改为在 NSRunLoop mainRunLoop 下运行。像魔术一样工作。但是,我几乎可以肯定,根据我的主线程,辅助线程中的运行循环有 10 个不同的级别错误

所以我的问题分为两部分,我希望没问题。

  1. 我为了让辅助线程运行并通过运行循环响应事件而应用的小“hack”可能出了什么问题?

  2. 配置辅助线程以监听所有基于事件/计时器的源的正确方法是什么,这样我就不必执行第 1 步。

感谢大家的指点。

最佳答案

以相反的顺序回答您的问题:

2。你有两个问题。 -[NSRunLoop run] 的文档说:

If no input sources or timers are attached to the run loop, this method exits immediately; otherwise, 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.

因此使用线程自己的运行循环,可能没有为运行循环定义的输入源,因此它会立即返回。如果有,您的运行循环将无限循环,并且该点之后的其余代码永远不会被执行。

为了让事情正常运行,您的运行循环首先需要有一些输入源,然后需要定期运行以检查事件。请注意,您不想使用 [NSRunLoop run],因为您永远无法取回控制权。相反,我建议在 return YES 之前设置一个循环,该循环持续运行运行循环,直到线程被取消,或者直到您完成流式传输数据。这将允许运行循环在数据到达时对其进行处理。像这样:

while (!done) {
    [myRunLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];
}

[NSRunLoop runUntilDate:] 处理事件直到指定的日期,然后将控制权返回给您的程序,以便您可以做任何您想做的事情。

1。它之所以有效,是因为您的主线程的运行循环正在定期运行,因此正在处理事件。但是,如果您的主线程曾经阻塞,您的数据将停止到达。如果主线程正在等待来自您的第二个线程的数据,这可能会特别糟糕。此外,NSRunLoop 不是线程安全的:

Warning: The NSRunLoop class is generally not considered to be thread-safe and its methods should only be called within the context of the current thread. You should never try to call the methods of an NSRunLoop object running in a different thread, as doing so might cause unexpected results. (from the NSRunLoop documentation.)

Apple 的线程编程指南中有一节标题为 Run Loop Management ,这在一定程度上解释了所有这些。这不是我读过的最清晰的文档,但如果您正在使用运行循环,那么它是一个很好的起点。

关于iphone - 主线程的 NSRunLoop 在辅助线程中被引用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/924112/

相关文章:

iphone - 单个 float 分配是 iPhone 上的原子操作吗?

iphone - UIscrollview调整大小问题

iPhone - 如何从应用程序保存用户设置?

ios - 从 objective-c 中的日期数组中分离月份和年份

objective-c - NSMutableArray 和 NSDictionary 的内存泄漏

iphone - 对核心数据对象进行分组

iphone - 目标指定产品类型 'com.apple.product-type.framework'

ios - 如何阻止 Sprite Kit 使用 iAd 横幅重新初始化场景

objective-c - 即时通讯API

iphone - 缩短 UIScrollView 中的触摸延迟?