What does NSRunLoop do?

onmyway133 picture onmyway133 · May 10, 2013 · Viewed 7.8k times · Source

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 ?

Answer

FluffulousChimp picture FluffulousChimp · May 11, 2013

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