performSelector:onThread打破了runloop?

时间:2013-06-07 07:57:28

标签: iphone objective-c performselector runloop

我不确定如何使用API​​ performSelector:onThread,我需要一些建议 据我所知,我需要一个runloop来调用performSelector:onThread,所以我做了一个。但后来我发现了一个问题:一旦我拨打performSelector:onThreadrunloop就停止了。

这是我的测试代码,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;
}

终端日志结果如下,“线程退出....”未打印。有谁告诉我问题出在哪里?

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 ... 

2 个答案:

答案 0 :(得分:0)

线程没有被破坏。 它只是停留在mach_msg_trap

如果没有输入源或定时器附加到运行循环,则此方法立即退出;否则,它在NSDefaultRunLoopMode中运行接收器

perfromSelector:onThread导致调用者向目标线程添加输入源。 所以方法[NSRunLoop run]实际上会调用[NSRunLoop(NSRunLoop)runMode:beforeDate:] 并发送无限日期作为之前的日期参数。

如果你想停止线程,请使用[NSRunLoop(NSRunLoop)runMode:beforeDate:]代替,并发送一个有限的日期,如下所示:

BOOL shouldKeepRunning = YES;        // global
NSRunLoop *theRL = [NSRunLoop currentRunLoop];
while (shouldKeepRunning && 
    [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);

答案 1 :(得分:0)

这将有助于确切地知道您要在这里完成什么,因为元问题是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 loopsdocumentation

  

如果没有输入源或定时器附加到运行循环,这个   方法[run]立即退出;否则,它运行接收器   NSDefaultRunLoopMode通过反复调用runMode:beforeDate:。在   换句话说,这种方法有效地开始了无限循环   处理来自运行循环的输入源和定时器的数据。