performSelector:onThread 中断运行循环?
Posted
技术标签:
【中文标题】performSelector:onThread 中断运行循环?【英文标题】:performSelector:onThread breaks runloop? 【发布时间】:2013-06-07 07:57:27 【问题描述】:我不确定如何使用 API performSelector:onThread
,在这里我需要一些建议。
据我所知,我需要一个runloop
来拨打performSelector:onThread
的电话,所以我做了一个。但是后来我发现了一个问题:一旦我调用了performSelector:onThread
,runloop
就会停止。
这是我的测试代码,runloop 在function kickOffThread
。
- (void)setCurrentThread
self.thread = [NSThread currentThread];
- (void)kickOffThread
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[self performSelector:@selector(setCurrentThread) withObject:nil afterDelay:0];
while (!threadCheck)
NSLog(@"threadCheck = %d", threadCheck);
[[NSRunLoop currentRunLoop] run];
[self doSomeCleanUp];
NSLog(@"thread exit ....");
[pool release];
我打电话给kickOffThread
performInBackground
:
[self performSelectorInBackground:@selector(kickOffThread) withObject:nil];
在 mainThread 中我调用以下 API 将 threadCheck 设置为 YES,它被正确调用,但我的线程循环突然停止。
[self performSelector:@selector(signalThreadCheck) onThread:self.thread withObject:nil waitUntilDone:NO];
-(void)signalThreadCheck
NSLog(@" signalThreadCheck ... ");
threadCheck = YES;
终端日志结果如下,“thread exit ....”没有打印出来。谁能告诉我问题出在哪里?
2013-06-07 15:51:54.827 MBSMapSample[23582:17403] threadCheck = 0
2013-06-07 15:51:54.827 MBSMapSample[23582:17403] threadCheck = 0
2013-06-07 15:51:54.827 MBSMapSample[23582:17403] threadCheck = 0
2013-06-07 15:51:54.836 MBSMapSample[23582:17403] threadCheck = 0
2013-06-07 15:51:54.837 MBSMapSample[23582:17403] threadCheck = 0
2013-06-07 15:51:54.837 MBSMapSample[23582:17403] threadCheck = 0
2013-06-07 15:51:54.837 MBSMapSample[23582:17403] threadCheck = 0
2013-06-07 15:51:54.837 MBSMapSample[23582:17403] threadCheck = 0
2013-06-07 15:51:54.837 MBSMapSample[23582:17403] threadCheck = 0
2013-06-07 15:51:54.840 MBSMapSample[23582:17403] threadCheck = 0
2013-06-07 15:51:54.844 MBSMapSample[23582:17403] threadCheck = 0
2013-06-07 15:51:54.846 MBSMapSample[23582:17403] signalThreadCheck ...
【问题讨论】:
【参考方案1】:线程没有中断。 它只是卡在了 mach_msg_trap
如果没有输入源或计时器附加到运行循环,则此方法立即退出;否则,它将在 NSDefaultRunLoopMode 中运行接收器
perfromSelector:onThread 导致调用者将输入源添加到目标线程。 所以方法 [NSRunLoop run] 实际上会调用 [NSRunLoop(NSRunLoop) runMode:beforeDate:] 并发送一个无限日期作为 beforeDate 参数。
如果您想停止线程,请改用 [NSRunLoop(NSRunLoop) runMode:beforeDate:],并发送一个有限的日期,如下所示:
BOOL shouldKeepRunning = YES; // global
NSRunLoop *theRL = [NSRunLoop currentRunLoop];
while (shouldKeepRunning &&
[theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);
【讨论】:
使用 '[NSRunLoop(NSRunLoop) runMode:beforeDate:]' 正是我所需要的!无论如何,我还是不太明白为什么线程卡在我的第一个场景中,有相关信息的链接吗? 如果运行循环中有输入源。 [NSRunLoop run] 实际上会调用 [NSRunLoop runMode:beforeDate:] 并发送一个无限日期作为 beforeDate 参数。这会导致runloop无限运行。您可以在signalThreadCheck打印后暂停您的应用程序,您将看到堆栈卡在mach_msg_trap中【参考方案2】:这将有助于准确地了解您要在这里完成什么,因为元问题是 NSThread
是否是完成任务的正确工具。 GCD 通常是首选。
不过,要考虑的事情之一是NSRunLoop
是否有输入源。让我们看一个稍微简化的例子,我们分离一个新的NSThread
,如下所示:
- (void)viewDidLoad
[super viewDidLoad];
_threadCheck = NO; // this is an ivar
[NSThread detachNewThreadSelector:@selector(kickOffThread) toTarget:self withObject:nil];
double delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void)
for( uint8_t i = 0; i < 10; i++ )
[self performSelector:@selector(logSomething) onThread:self.thread withObject:nil waitUntilDone:NO];
[self performSelector:@selector(signalThreadCheck) onThread:self.thread withObject:nil waitUntilDone:NO];
);
- (void)kickOffThread
_thread = [NSThread currentThread]; // this is an ivar
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
while( !_threadCheck && [runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]] );
[self doSomeCleanUp];
printf("thread exit...\n");
- (void)doSomeCleanUp
printf("cleaning up...\n");
- (void)logSomething
printf("in background thread...\n");
-(void)signalThreadCheck
printf("signalThreadCheck\n");
_threadCheck = YES;
在控制台上,我得到:
cleaning up...
thread exit...
为什么从未调用过logSomething
?因为我们生成的后台线程的运行循环没有输入源,所以它立即退出。例如,如果我们添加带有[NSPort port]
的输入源,我们可以保持衍生线程的运行循环转动,例如:
- (void)kickOffThread
_thread = [NSThread currentThread];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addPort:[NSPort port] forMode:NSRunLoopCommonModes];
while( !_threadCheck && [runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]] );
[self doSomeCleanUp];
printf("thread exit...\n");
现在我们将以下内容打印到控制台:
in background thread...
in background thread...
in background thread...
in background thread...
in background thread...
in background thread...
in background thread...
in background thread...
in background thread...
in background thread...
signalThreadCheck
cleaning up...
thread exit...
More about run loops 和来自documentation:
如果没有输入源或定时器附加到运行循环,这 方法 [run] 立即退出;否则,它将在 NSDefaultRunLoopMode 通过重复调用 runMode:beforeDate:.在 换句话说,这个方法有效地开始了一个无限循环, 处理来自运行循环的输入源和计时器的数据。
【讨论】:
以上是关于performSelector:onThread 中断运行循环?的主要内容,如果未能解决你的问题,请参考以下文章