Run-loops vs. Threads in Cocoa

As a relative newby to the world of Cocoa programming (on the iPhone in particular), I have spent some time trying to understand if and when you’d use a run-loop instead of launching a separate thread. I was unable to find any definitive answer on the web, so ended up joining the dots myself. What follows is my understanding of when you’d want to use one or the other. Cocoa experts are welcome to comment if I’ve got it wrong.

The Problem

Touches aren’t the only source of input to an iPhone application. For example, another source can be a socket – sometime you want to listen to a socket for data. But you don’t want the UI to lock up whilst it’s listening – you still want input from the user to be dealt with promptly. Similarly, you might want events to be triggered automatically at certain time intervals, but without locking up the application in the interim.

Coming from other UI frameworks, you might think that the way to deal with this is to to use a separate thread. That way, the thread can block on the socket or sleep for a particular time interval. However, as we all know, the introduction of multiple threads immediately introduces a bunch of potential defects that are difficult to reproduce and fix.

The Solution

Enter run loops. Or more specifically, the run loop – each iPhone application has one by default and for our purposes, this is all we need.

So what exactly is a run loop?

Well, first consider this assertion:the vast majority of the time that your Cocoa application is running, it’s doing nothing. More specifically, it’s waiting for input. However, as soon as you touch the screen, an event gets triggered, which may in turn result in some of your code being executed. If some data comes into a socket, or a timer fires, the same applies.

The key things is that once this code has been executed, the application goes back to waiting for input. Furthermore, in many cases the execution time of your code will be very small relative to the time the application spends waiting for input.

I think of run loops as a mechanism that exploits this fact.

A run loop is essentially an event-processing loop running on a single thread. You register potential input sources on it, pointing it to the code that it should execute whenever input is available on those sources.

Then when input comes into a particular source, the run loop will execute the appropriate code, then go back to waiting for input to come in again to any of it’s registered sources. If input comes into a registered source whilst the run-loop is executing another piece of code, it’ll finish executing the code before it handles the new input.

The upside of this is that whilst you mightn’t know exactly what order things are going to come in, at least you know that they’ll be processed one after the other instead of in parallel. This means that you avoid all of those nasty multi-threading issues that were described earlier. And that’s why run loops are useful.

Run loop scheduling in action

By default, all touch events received by an iPhone application are queued for processing by the application’s main run loop, so there’s nothing special you need to do for UI components. However, other sources of input require additional coding.

To schedule an NSInputStream on a run loop, you’d do something like this:


...
[iStream setDelegate:self];
[iStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
...

This code sets it up so that whenever input is available on ‘iStream’, a ‘stream:handleEvent’ message will be sent to ‘self’. Note that the stream could be from any sort of source, including a socket.

Another object that can be scheduled on a run loop is a timer. For example:


[NSTimer scheduledTimerWithTimeInterval:2.0
target:self
selector:@selector(doStuff)
userInfo: nil
repeats:YES];

will schedule a timer on the current run loop to send a ‘doStuff’ message to ‘self’ every two seconds.

When not to use a run loop

So when wouldn’t you use a run loop? Well, if you had some event-handling code that was going to take a long time to execute (for example, performing some CPU-intensive calculation), then everything else in the event-handling queue won’t get handled until it’s finished. This would cause your application to become unresponsive until the processing has finished. In that sort of scenario, you might want to consider using a separate thread to do the processing.

However, for the vast majority of cases, our code for handling events – be they from the screen, sockets or timers – takes a very short time to execute. And that’s why it’s easier (and safer) to just use the main run loop to handle those events.

The trade-offs

The only downside to using a run loop instead of a thread is that instead of just whacking a thread around a whole section of code that you know will block in one or more places, you have to go to each potential blocking point, register the source on the run loop, and implement a callback to process events that are generated from that source.

Whilst this may seem like some effort, it pales in comparison to the pain that can result from poorly-considered threading. So next time you’re tempted to use a thread to read from a blocking input source, consider taking the time to use a run loop. It could well save you a lot of time in the long run.

About Ben Teese

I'm a Senior Consultant at Shine Technologies. You can find me on Twitter and GitHub.
This entry was posted in iPhone, Mac. Bookmark the permalink.

8 Responses to Run-loops vs. Threads in Cocoa

  1. Adi says:

    Hi, thanks for your bried explanation. I too couldn’t find a good explanation about the difference between thread (NSTask) and RunLoop from the web. It’s getting clearer right now, but I still have a question or two.

    For example: I’m creating a simple program that is using a NSTimer as a counter. The timer fire the selector (that adds up the counter value) every certain interval defined in the method. Let’s say Wait(100), means wait for 100 mSec. then the Wait method will check the counter if it’s already 100 msec (in this case, interval is 0.01). I assume you have to use a task or thread in order to do this. Is this correect, or you could still use a runloop?

  2. Ted Neward says:

    Ben,
    I think you got this all wrong. I see run loops on the iPhone, or within the Cocoa context, as analogous to the “main game loop” in the context of game programming. Games loops have the following properties: they are tight, the are efficient, and they run well. To that end, game loops execute the usual process events (inputs, game events, etc.), update (physics engine update, sprite/game object updates), followed by the all important render. All of this is usually locked into a tight frame rate of say 60Hz, that is 1/60 frames per second.

    Now that is tight, just like run loops.

  3. Goman says:

    Hi Ben,

    Thank you very much for your explanation.
    I was spent time to find what’s is the run loop in the internet, but I can’t find any one which is very clear until I get your blog.

    Do you mind if I translate this article to Chinese?
    Goman

  4. Dhiraj says:

    Thanks for the nice tutorial, I had bit confusion between Threads and Run loop. Now is very much clear.

    Can you post a complete code example for the same ?


    Dhiraj

  5. jack says:

    i have a question.
    when i use NSURLConnection asynchronously, is it using the main run loop?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s