Question

I'm not sure how to use the API performSelector:onThread and I need some suggestions here.
As far as I known, I need a runloop to make the call of performSelector:onThread, so I made one. But then I find a problem : once I called performSelector:onThread, the runloop stops.

Here 's my test code, runloop is in 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];
}

I call kickOffThread by performInBackground:

   [self performSelectorInBackground:@selector(kickOffThread) withObject:nil];

In mainThread I call following API to set threadCheck to be YES, it is correctly called, but my thread loop suddenly stops.

[self performSelector:@selector(signalThreadCheck) onThread:self.thread withObject:nil waitUntilDone:NO];
-(void)signalThreadCheck
{
    NSLog(@" signalThreadCheck ... ");
    threadCheck = YES;
}

terminal log result is as follows, "thread exit ...." not printed. Anyone tells me where is the problem ?

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 ... 
Was it helpful?

Solution

the thread is not breaked. it just stucked in mach_msg_trap

If no input sources or timers are attached to the run loop, this method exits immediately; otherwise, it runs the receiver in the NSDefaultRunLoopMode

the perfromSelector:onThread cause the caller add a input source to the target thread. so the method [NSRunLoop run] will actually call [NSRunLoop(NSRunLoop) runMode:beforeDate:] and send a infinite date as beforeDate param.

if you want to stop the thread, use [NSRunLoop(NSRunLoop) runMode:beforeDate:] instead, and send a limited date, like this:

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

OTHER TIPS

It would help to know exactly what you're trying to accomplish here, because the meta-question is whether NSThread is the right tool for the task. GCD is often preferred.

Nonetheless, one of the things to consider is whether the NSRunLoop has an input source or not. Let's take a slightly simplified case where we detach a new NSThread as follows:

- (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;
}

On the console, I get:

cleaning up...
thread exit...

Why was logSomething never called? Because the run loop for the background thread that we spawned has no input source, so it exits immediately. If we add an input source with [NSPort port] for example, we can keep the spawned thread's run loop turning, e.g.:

- (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");
}

Now we print the following to the console:

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 and from the documentation:

If no input sources or timers are attached to the run loop, this method [run] exits immediately; otherwise, it runs the receiver in the NSDefaultRunLoopMode by repeatedly invoking runMode:beforeDate:. In other words, this method effectively begins an infinite loop that processes data from the run loop’s input sources and timers.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top