Queued Background Tasks for Cocoa

The megahertz race is over, and instead we get more execution cores. This means that we as developers must make our applications parallel, in order to take advantage of the new performance. The easiest way to be parallel is to execute tasks in new threads, something that is useful also for lengthy but not resource intensive tasks such as network access.

Any sane developer avoids premature optimization, so the task we later want to execute in a separate thread is almost always available in our current context. I am sure all Java-developers has seen something like this:

And often an anonymous Runnable is involved as well. Executing a task in a separate thread with Cocoa and Cocoa Touch is ridiculously easy given the dynamic nature of Objective-C. This is done with Cocoa with this one-liner:

The performSelectorInBackground:withObject: is a companion method for performSelector:withObject:, and will create a new NSThread instance, and execute the specified method there. There are more companions such as performSelectorOnMainThread:withObject: that is used to execute a method on the main UI thread.

This easy access to executing in new threads often can result in code that spawns too many threads. In Cocoa on Mac OS X this is seldom a problem, not for Cocoa Touch applications under development when running in the iPhone simulator either. But on the more resource constrained iPhone and iPod Touch devices this will be a problem. Spawning a dozen background threads to download images will bring the iPhone OS to a staggering mess.

Update: I have removed CWSelectorOperation from this post, since using NSInvocationOperation is preferred.

Operation Queues

The solution is to use a NSOperationQueue, that as the name implies handles a queue of operations. The max number of concurrent operations can be set, as well as dependencies and priority. The downside is that NSOperationQueue can only handle instances of NSOperation.

NSOperation is an abstract class, where you as a developer override the main method to execute your task. In short NSOperation is programatically the equivalent of the Java Runnable interface. It is even worse as Objective-C do not support anonymous classes, so we will be required to actually implement our queued tasks as separate classes.

Not as neat as one would like, and definitely not even very Cocoa-like.

What is Missing

As a Cocoa developer I expect that executing a task on background queue, would be just as easy as executing a task on a background thread. The expected needed code is something like this:

To make this work we need:

  1. A category on NSObject to add performSelectorOnBackgroundQueue:withObject:.
  2. Implement a concrete subclass of NSOperation to support execution of a selector on any target.Use NSInvocationOperation to perform selector.
  3. A category on NSOperationQueue to add a shared operation queue.

This is in the order of how a client developer would see it (most developers will only care for the NSObject category). For us this is the reverse order when implement this functionality.

A Shared Operation Queue

Cocoa and Cocoa Touch used many shared instances, for example a shared UIAccelerometer instance, and a shared NSURLCache instance. So this pattern of single point dependency injection is common in Cocoa and works very well. So let us follow suit by introducing an interface like this:

The shared operation queue will be created lazily if it do not exist, and the client can easily swap it out at startup if needed. The implementation is very small, and a good example of how we should implement class variables with proper memory handling.

Enter CWSelectorOperation

Update: CWSelectorOperation is no longer needed, our implementation instead uses NSInvocationOperation.

Wrapping up NSObject

And at last we reach the category on NSObject, that is what we initially wanted, and what 95% of all use cases will be limited to.

Most performSelector* methods do not have a return value, I have chosen to return the resulting NSInvocationOperation so that it can be used to setup dependencies for future queued operations. I have also added a second method performSelectorInBackgroundQueue:withObject:dependencies:priority: so that such dependencies can easily be setup, as well as prioritisation of operations.

The actual implementation is a simple wrapper around the NSInvocationOperation class, and category on NSOperationQueue that we implemented above:

And that is it, if you want to avoid cut and paste of all this code to your own project just download the source code here. And happy concurrent hacking.

Download the older source code here, with a concrete subclass of NSOperation called CWSelectorOperation.

4 Comments

  1. Marc Love

    Thanks Fredrik, this is an excellent resource…especially for those of us just getting started with iPhone development and unfamiliar with such resource-constrained environments.

    This should be really helpful for an app I’m working on that makes heavy use of some web services.

  2. John

    I’m getting the following error when using:

    [self performSelectorInBackgroundQueue:@selector(methodName:) withObject:nil];

    bool _WebTryThreadLock(bool), 0x487aa0: Tried to obtain the web lock from a thread other than the main thread or the web thread. This may be a result of calling to UIKit from a secondary thread. Crashing now…

    • The reason is that all methods in UIKit should be called on the main thread. So updating the UI directly from the background task is not a good thing, sometimes it works, sometimes you crash, and sometimes just nothing happens. This is why the Apple documentation says you must always call fromt he main thread.

      Try to use performSelectorOnMainThread:withObject:waitUntilDone:.

  3. MGC

    Great example. It looks like what I need.

    So how do I integrate this into my project code?
    I included the files into my project, but how do I call it from my view controller?

    Do I #import on every View Controller where I want to use this? If I do that will it all be the same queue?

    Essentially I want to use this to line up all of my methods in order no matter where I call them in my project. But they will sequentially execute based upon the order they were invoked.

    Right now it looks like I need to include this file in every view controller, but I am not sure if that is the right thing to do.

Leave a Reply