我最近刚试着用一个样本应用试图让我的脑袋完全缠绕NSRunLoop
.我编写的示例创建了一个简单的辅助线程via 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]
说:
如果没有输入源或定时器附加到运行循环,则此方法立即退出; 否则,它
NSDefaultRunLoopMode
通过反复调用来运行接收器runMode:beforeDate:
.换句话说,这种方法有效地开始了一个无限循环,它处理来自运行循环的输入源和定时器的数据.
因此,使用线程自己的运行循环,可能没有为运行循环定义输入源,因此它会立即返回.如果存在,则运行循环无限循环,并且在该点之后的其余代码永远不会被执行.
为了使事情正常工作,您的运行循环首先需要一些输入源,然后需要定期运行以检查事件.请注意,您不想使用[NSRunLoop run]
,因为您永远无法控制.相反,我建议在return YES
不断运行运行循环之前设置一个循环,直到线程被取消,或者直到你完成流数据.这将允许运行循环在数据到达时处理数据.像这样的东西:
while (!done) { [myRunLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]]; }
[NSRunLoop runUntilDate:]
将事件处理到指定的日期,然后将控制权返回给您的程序,以便您可以执行任何操作.
1
.它的工作,因为你的主线程的运行循环是周期性地运行,因此事件得到处理.但是,如果您的主线程被阻止,您的数据将停止到达.如果主线程正在等待第二个线程的数据,这可能会特别糟糕.此外,NSRunLoop不是线程安全的:
警告:NSRunLoop类通常不被认为是线程安全的,并且只应在当前线程的上下文中调用其方法.您永远不应该尝试调用在不同线程中运行的NSRunLoop对象的方法,因为这样做可能会导致意外结果.(来自NSRunLoop文档.)
Apple的线程编程指南有一个名为Run Loop Management的部分,它在一定程度上解释了所有这些.这不是我读过的最清晰的文档,但是如果你正在使用run loop,那么这是一个很好的起点.