Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What does NSRunLoop do?

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 ?

like image 833
onmyway133 Avatar asked May 10 '13 07:05

onmyway133


2 Answers

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
like image 196
FluffulousChimp Avatar answered Nov 03 '22 10:11

FluffulousChimp


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.

like image 31
David H Avatar answered Nov 03 '22 09:11

David H