I read many posts about NSRunLoop
, like this, this, this. But can't figure out what NSRunLoop
actually does
What I usually see is a worker thread
wthread = [[NSThread alloc] initWithTarget:self selector:@selector(threadProc) object:nil];
[wthread start];
with a NSRunLoop inside it
- (void)threadProc
{
NSAutoreleasePool* pool1 = [[NSAutoreleasePool alloc] init];
BOOL isStopped = NO;
NSRunLoop *runloop = [NSRunLoop currentRunLoop];
[runloop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
while (!isStopped)
{
{
NSAutoreleasePool* pool2 = [[NSAutoreleasePool alloc] init];
[runloop runMode:NSDefaultRunLoopMode
beforeDate:[NSDate distantFuture]];
[pool2 release];
}
}
[pool1 release];
}
And the main thread passes some work to this wthread
[self performSelector:@selector(someWork:) onThread:wthread withObject:nil waitUntilDone:NO];
In term of passing work from the main thread to the worker thread, I see many people do this. Why need NSRunLoop here ? What does it do ?
I read that NSRunLoop
is used to manage events, why is there nothing except calling runMode inside threadProc
?
The example you've shown is a Cocoa idiom for creating a thread that will continue to run after the method -threadProc
exits. Why?
Because:
NSRunLoop
instance you've created has at least one input source ([NSMachPort port]
)runMode:beforeDate
Without adding an input source and explicitly starting the run loop, the thread would terminate.
Parenthetically, although run loops are still vital for managing events and certain asynchronous tasks, I would not view NSThread
as the default way of architecting most asynchronous work in a Cocoa app nowadays. GCD is a far cleaner way of encapsulating background work.
EDIT:
Submitting work to a serial queue in GCD:
@interface Foo : NSObject
@end
@implementation Foo {
dispatch_queue_t _someWorkerQueue;
}
- (id)init {
self = [super init];
if( !self ) return nil;
_someWorkerQueue = dispatch_queue_create("com.company.MyWorkerQueue", 0);
return self;
}
- (void)performJob {
dispatch_async(_someWorkerQueue, ^{
//do some work asynchronously here
});
dispatch_async(_someWorkerQueue, ^{
//more asynchronous work here
});
}
@end
Much goes on behind the scene. The reason for this is it provides a way for the thread to stop executing when there are no work items. If you ever used a real time OS, tasks need a place to give up the processor so others can run.
What is not well documented is that when you send performSelector:onThread:..., it's the run loop that queues the message and the wakes up to let the thread process it. If you add log messages to the while loop you can see this happen.
For the really curious there is sample code on github you con get to play around with run loops - add a comment and I'll list a few.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With