0

我不确定如何使用 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];
}

我打电话kickOffThreadperformInBackground

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

2 回答 2

0

线程没有被打破。它只是卡在了 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]]);
于 2013-06-07T09:23:36.623 回答
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...

有关运行循环文档的更多信息:

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

于 2013-06-07T10:08:10.723 回答