我不确定如何使用API performSelector:onThread
,我需要一些建议。
据我所知,我需要一个runloop
来调用performSelector:onThread
,所以我做了一个。但后来我发现了一个问题:一旦我调用performSelector:onThread
, 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];
}
我通过performInBackground
调用kickOffThread
:
[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 ...
线程未中断。它只是卡在mach_msg_trap
如果没有输入源或计时器附加到运行循环,此方法立即退出;否则,它在nsdefaulunloopmode
中运行接收器。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]]);
这将有助于确切地知道您在这里要完成什么,因为元问题是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("signalThreadCheckn");
_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...
更多关于运行循环和从文档:
如果没有输入源或计时器附加到运行循环,则此方法[run]立即退出;方法中运行接收器nsdefaulultrunloopmode通过重复调用runMode:beforeDate:。在换句话说,这个方法实际上开始了一个无限循环处理来自运行循环的输入源和计时器的数据。