6

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 ?

Community
  • 1
  • 1
onmyway133
  • 45,645
  • 31
  • 257
  • 263
  • I think of it as a fancy while(true){ //insert job to be done here } , where you can dynamically add code to be executed (events, work, sockets etc.). The runloop not really empty, a port has been attached to the runloop, it's a form of interprocess communication. – foFox May 10 '13 at 07:56
  • I found this http://bou.io/RunRunLoopRun.html to be useful as well – onmyway133 Jun 17 '15 at 17:17

2 Answers2

5

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
FluffulousChimp
  • 9,157
  • 3
  • 35
  • 42
  • 1) So why is there while loop inside threadProc? 2) GCD will transfer work to any available/unknown threads, but NSRunLoop will ensure all work will be transfer to this same worker thread, right ? – onmyway133 May 12 '13 at 00:57
  • To provide a mechanism for terminating the run loop - when the flag `isStopped` is set (though that's not shown in the code...) – FluffulousChimp May 12 '13 at 09:09
  • 2) see edited answer for GCD approach to the use case you're describing. – FluffulousChimp May 12 '13 at 09:22
  • does dispatch_queue ensure that 2 work will be done in the same thread serially ? – onmyway133 May 13 '13 at 02:35
  • Yes, from the docs _"All blocks submitted to dispatch queues are dequeued in FIFO order. By default, queues created with `dispatch_queue_create()` wait for the previously dequeued block to complete before dequeuing the next block."_ – FluffulousChimp May 13 '13 at 10:09
  • 1
    @onmyway133 No serial queue does no garantee that all block will be done in the same thread. it garanties only serial execution – hash3r Jul 25 '16 at 21:29
  • @FluffulousChimp after reading this I fee,, I have a better understanding of the runloop. Is it correct to say, to understand runloop, you need to drop down to the level of threads, instead of queues and operations from the GCD and Operations level. After reading this I dropped down to level of thread and tried calling performselectors on this thread and they did not work. But once I start a runloop for the thread, my performselectors start working. So I assume runloops do this handling, behind the scene. – Rohan Bhale Dec 28 '18 at 05:15
4

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.

David H
  • 40,852
  • 12
  • 92
  • 138
  • 1) but inside the threadProc, there is a while loop, so the thread will be always busy? How can NSRunLoop tells it to stop executing? 2) AFAIK, this is a mechanism to ensure that many call to performSelector:onThread: will transfer work to this same worker thread, right ? – onmyway133 May 11 '13 at 03:52
  • 1) sorry to not be clear - the OS will suspend the thread inside runMode: if there is nothing for your code to process. 2) it's one feature of the system - but there are others - port and timer processing. You will learn the most by reading the apple docs on CFRunloop - I believe there is a guide on it. – David H May 11 '13 at 11:45