Question

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 ?

Was it helpful?

Solution

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:

  • the NSRunLoop instance you've created has at least one input source ([NSMachPort port])
  • you've explicitly started the run loop with 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

OTHER TIPS

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.

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