Reputation: 2115
I am trying to understand the concept of RunLoops. I have read Apple developer's guide on RunLoops and to some extent I have understood the concept of RunLoops. To make my concept more clearer I wrote a very simple code which uses RunLoops in it. The code can be seen below.
- (void)viewDidLoad
{
[super viewDidLoad];
thread = [[NSThread alloc] initWithTarget:self selector:@selector(testMethod) object:nil];
[thread start];
}
- (void)testMethod {
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
NSLog(@"Thread Entered");
NSMachPort* dummyPort = [[NSMachPort alloc] init];
[[NSRunLoop currentRunLoop] addPort:dummyPort forMode:NSDefaultRunLoopMode];
while(!exitThread) {
NSLog(@"Thread did some work");
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
[[NSRunLoop currentRunLoop]
removePort:dummyPort
forMode:NSDefaultRunLoopMode];
[dummyPort release];
NSLog(@"Thread Exited");
[pool drain];
}
- (IBAction)doDomeWorkOnBackgroundThread:(id)sender {
[self performSelector:@selector(dummyMethod) onThread:thread withObject:nil waitUntilDone:NO];
}
- (IBAction)exitThread:(id)sender {
[self performSelector:@selector(exitBackgroundThread) onThread:thread withObject:nil waitUntilDone:NO];
}
- (void)exitBackgroundThread {
exitThread = YES;
}
- (void)dummyMethod {
//Empty
}
In above code I am creating a background thread and on that background thread I am calling function testMethod
. Inside testmethod
I am running a while which checks for BOOL variable exitThread
and runs the background thread's RunLoop using the - (BOOL)runMode: beforeDate:
method of NSRunLoop.
There are two IBActions which are attached to two buttons. As the name of the IBActions suggest one of them is to exitThread
and other one is to wake up the thread and execute NSLog statement written in the while loop.
The above code runs just as I expected. Whenever doDomeWorkOnBackgroundThread
method is executed thread wakes up from its runloop,performs the next iteration of the while loop, check for BOOL variable exitThread
and on finding it value as false goes inside the while loop and executes the NSlog statement.
Similarly When exitThread:
method is executed the exitThread
variable is set to true which causes the while loop and the thread to exit.
However I need few more clarifications :
1) If instead of using runMode: beforeDate:
in the while loop , I use run
or runUntilDate:
method of NSRunLoop, then the thread is never exited when exitThread:
method is executed. The exitBackgroundThread method is called on the background thread but the while loop never performs it's next iteration(as it does when I use runMode: beforeDate:
) and hence the thread is never exited.
2) I tried changing exitBackgroundThread
method to
- (void)exitBackgroundThread {
exitThread = YES;
CFRunLoopStop(CFRunLoopGetCurrent());
}
Since exitBackgroundThread
is executed on background thread CFRunLoopGetCurrent() should give the RunLoop of background thread. So this should ideally stop the run loop of the background thread regardless of whatever method of NSRunLoop I am using to start the RunLoop. So in any case thread must exit on call of above function. But it is just not happening.
I know I am missing something here and I am doing a fair amount of googling on my part to find answer to this question but just cant seem to find the right answer.
**EDIT
I have found this question which is very similar to my first query. It clears my first doubt to much extent.
Upvotes: 4
Views: 1792
Reputation: 359
Documents of runMode:beforeDate:
say:
Runs the loop once, blocking for input in the specified mode until a given date.
That mean it processes '@selector' input source only one time and then returns, not like run
will continue to process next input source without returning.
However runUntilDate:
will return after limit date if you code it as:
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:5.0]];
Upvotes: 1
Reputation: 386038
The change you see when you use run
or runUntilDate:
instead of runMode:beforeDate:
is expected. The documentation for runMode:beforeDate:
says this:
it returns after either the first input source is processed or
limitDate
is reached.
There is an input source responsible for handling the performSelector:...
requests. So when you send performSelector:...
, the run loop processes an input source and then returns.
On the other hand, the documentation for run
says this:
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.
So after the run loop processes the input source for your performSelector:...
request, it waits for another input source to be ready. It doesn't return. Since it doesn't return, your while
loop never gets a chance to test exitThread
.
Your attempt to use CFRunLoopStop
is a good idea, but unfortunately, the documentation for run
says this:
If you want the run loop to terminate, you shouldn't use this method. Instead, use one of the other run methods and also check other arbitrary conditions of your own, in a loop.
So you can't rely on CFRunLoopStop
to make run
return.
Instead, you can drop to a lower level and use CFRunLoopRun
to run the run loop, because CFRunLoopStop
is documented to make that function return:
This function forces
rl
to stop running and return control to the function that calledCFRunLoopRun
orCFRunLoopRunInMode
for the current run loop activation.
So try this to run your run loop:
while(!exitThread) {
NSLog(@"Thread did some work");
CFRunLoopRun([NSRunLoop currentRunLoop].getCFRunLoop);
}
Upvotes: 3