我最近正在研究一个示例应用程序,试图让我的头脑完全围绕 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 个不同的级别错误。
所以我的问题分为两部分,我希望没问题。
我为了让辅助线程运行并通过运行循环响应事件而应用的小“hack”可能出了什么问题?
配置辅助线程以监听所有基于事件/计时器的源的正确方法是什么,这样我就不必执行第 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 invokingrunMode: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/