Asynchronous Programming in Cocoa Touch

It's unavoidable. Pretty much anywhere you go as a programmer these days concurrency is important. It's no less important on mobile platforms like the iPhone. In this post I intend to outline the fundamental techniques necessary for asynchronous programming on the iPhone. While these techniques could very well apply to standard, desktop Cocoa I'll focus on Cocoa touch.

I'll cover three basic approaches: NSObject's performSelectorInBackground message, NSThread and NSTimer.

NSObject's performSelectorInBackground message

NSObject's performSelectorInBackground message can be used to easily farm off tasks that run asynchronously. Here's an example that could be placed in a view controller.

-(void) workerThread {
	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 

	NSString *str = [NSString stringWithContentsOfURL:[NSURL URLWithString:@"http://www.chrisumbel.com"]];

	[pool release];  
}

- (void)viewDidLoad {
    [super viewDidLoad];

	[self performSelectorInBackground:@selector(workerThread) withObject:nil];
}

performSelectorInBackground simply executed workerThread asynchronously. Not much to it.

Essentially that just loaded a string with the contents of a web page asynchronously in about the most raw form possible in Cocoa. Obviously this is far more simplistic than anything you'd do for production but it is a potentially long-running, I/O-bound operation which makes it a fine candidate for asynchronous execution. Note that it was unconcerned with synchronization and did not communicate back with the main thread.

Check out the the use of NSAutoreleasePool. It's imperative that you create an NSAutoreleasePool initially and release it before you exit the thread. It's responsible for memory management in the thread body.

NSThread Basics

The most obvious class involved in asynchronous operations is NSThread which, as you would guess, effectively owns a separate thread of execution. Consider the following functional equivalent of the example above:

-(void)workerThread {
    // setup the thread's memory management.
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 

    // grab the contents of a web page
    NSString *str = [NSString stringWithContentsOfURL:[NSURL
        URLWithString:@"http://www.chrisumbel.com"]];
	
    [pool release];  
}

-(void)viewDidLoad {
    [super viewDidLoad];

    // spawn a worker thread
    [NSThread detachNewThreadSelector:@selector(workerThread) 
	toTarget:self withObject:nil];
}

Thread Communication

Now I'll alter the example slightly to communicate back to the main thread. This is useful because you nearly always want to execute UI code on the main thread. The following example simply outputs its results to a label.

-(void)updateContent:(NSString *)content {
    [outputLabel setText:content];
}

-(void)workerThread {
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 

    NSString *str = [NSString stringWithContentsOfURL:
        [NSURL URLWithString:@"http://www.chrisumbel.com"]];
    
    // send our results back to the main thread
    [self performSelectorOnMainThread:@selector(updateContent:)
        withObject:str waitUntilDone:NO];
    
    [pool release];  
}

-(void)viewDidLoad {
    [super viewDidLoad];

    [NSThread detachNewThreadSelector:@selector(workerThread) 
	toTarget:self withObject:nil];
}

It's NSObject's performSelectorOnMainThread message that facilitates the inter-thread communication. Arguments to the target message (updateContent in my case) are passed along via withObject.

Synchronization

Mutex behavior is accomplished via the NSLock class. The following example synchronizes appending the results of the URL-lookup to a local file.

NSLock *myLock = nil;

-(void)workerThread:(NSString *)urlString {
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 

    NSString *str = [NSString stringWithContentsOfURL:
	[NSURL URLWithString:urlString]];

    // block other threads	    
    [myLock lock];
	
    FILE *f = fopen("content.txt", "a+");
    fputs([content UTF8String], f);
    fclose(f);
	
    // stop blocking
    [myLock unlock];

    [pool release];  
}

-(void)viewDidLoad {
    [super viewDidLoad];

    // create the lock object
    myLock = [[NSLock alloc] init];
	
    // spawn worker thread 1
    [NSThread detachNewThreadSelector:@selector(workerThread:) 
	toTarget:self withObject:@"http://www.apple.com"];
    
    // spawn worker thread 2
    [NSThread detachNewThreadSelector:@selector(workerThread:) 
	toTarget:self withObject:@"http://www.gnome.org"];
}

NSTimer

The NSThread examples above were true, honest-to-goodness multi-threaded where each task was running in a separate NSRunLoop (Cocoa event handling loop). By contrast the following code uses an NSTimer running on the main run-loop to periodically execute a timed operation. NSTimers aren't perfectly accurate, however. You shouldn't rely on them being real-time.

NSTimer *timer = nil;
NSInteger timesExecuted = 0;

-(void)timerTick {
    timesExecuted++;
    [outputLabel setText:[timesExecuted stringValue]];
}

-(void)viewDidLoad {
    [super viewDidLoad];

    // create a timer that ticks every 10 seconds and executes timerTick
    // which I defined above
    timer = [NSTimer scheduledTimerWithTimeInterval: 10.0 target:self 
	selector:@selector(timerTick) userInfo:nil repeats: YES];
}

Conclusion

Well, there's the basics. For more depth you can check out Apple's iPhone Threading Programming Guide It's quite comprehensive and definitely worth the read.
Created on 2010-01-17 00:45:00
Share on Facebook Facebook
Comment Feed
Add a Comment: (HTML not accepted. URLs will automatically be converted to links)
Body
Nickname (Login || Register)
Home Page
Email Addy(kept private)
Are you human?
Tags:
linq .Net performance sql 2008 sql server powershell indexes scripting reporting services filestream ruby ironruby entity framework EF testing .net framework 4.0 ADO.NET SSRS rs setpolicies vb cte c# podcasts webdav exchange server data warehousing Data Services Web Services Astoria jQuery database object oriented cql refactoring remoting simpledb cloud HTML GObject GNOME Vala BI couchdb django ORM python erlang functional C curl stackless concurrency Groovy Java JVM dynamic tools windows ironpython dlr systems programming go CAPTCHA appengine natural language full-text rails lucene wave clr parallel virtualization Oracle iPhone xml Objective-C Haiku security cocoa touch C++ BeOS Operating Systems Lucene monitoring Solr lisp VS 2010
Blog History:
Solrnet, a Solr Client Library for .Net - 03/08/2010
Monitoring Solr with LucidGaze - 02/21/2010
Haiku, an Open Source Continuation of BeOS - 02/10/2010
Basic Authentication with a NSURLRequest in Cocoa Touch - 01/24/2010
Asynchronous Programming in Cocoa Touch - 01/17/2010
NSXML-like XPath Support in Cocoa Touch with TouchXML - 01/03/2010
Using Solr in Django for Full-Text Searching via Solango - 01/01/2010
Using Entity Framework with Oracle - 12/22/2009
Solutions to Common VirtualBox Problems - 12/20/2009
Parallel Programming with the Task Parallel Library and PLINQ in .Net 4.0 - 12/14/2009
Clojure, A Lisp for the JVM and CLR - 12/13/2009
Google Wave Robots in Java - 12/07/2009
Employing Solr/Lucene with SQL Server for Full-Text Searching - 12/05/2009
Full-Text Indexing in Ruby Using Ferret - 11/28/2009
Home-Brewing a Full-Text Search in Google's AppEngine - 11/22/2009
Using reCAPTCHA With Django - 11/21/2009
Phat Go Code Launched - 11/19/2009
A Little More of Google's Go - 11/17/2009
First Impressions of Go, Google's New Systems Language - 11/14/2009
Scripting Your .Net Applications with IronPython - 11/03/2009
Windows Services in Python - 11/02/2009
My Tool List - 10/26/2009
Groovy: Dynamic Language for the JVM... Groovy! - 10/23/2009
Easy Concurrency with Stackless Python - 10/03/2009
C from erlang via linked-in driver - 09/16/2009
Templating with NDjango - 09/06/2009
A little bit o' Erlang - 08/23/2009
Tale of a Website, from Rails to ASP.NET to Django - 08/20/2009
Now in Django - 08/19/2009
Stored Procedures in Django - 08/09/2009
CouchDBExtension - 08/06/2009
POCO Entities in ADO.NET 4.0 - 07/30/2009
Accessing SimpleDB from SSRS - 07/22/2009
Easy GNOME Development with the Vala Programming Language - 07/16/2009
HTML Parsing with Ruby and Nokogiri - 07/12/2009
Amazon SimpleDB Batched PUTs Usage and Performance - 07/10/2009
PowerShell 2.0 Out-GridView, ISE and ScriptCmdlets - 07/05/2009
Asynchronous and remote execution with powershell 2 ctp3 - 06/30/2009
Understanding Source Code with NDepend and CQL - 06/22/2009
Object Oriented Databases with db4o - 06/07/2009
ADO.Net Data Services with jQuery - 05/29/2009
Exchange webdav automation - 05/26/2009
Podcasts - 05/26/2009
Linq to Object Performance - 05/11/2009
SQL 2008 and powershell - 01/25/2009
SQL 2008 filtered indexes - 06/11/2008
SQL 2008's table valued parameters - 05/11/2008
SQL 2008's MERGE statement - 04/22/2008
ironruby - 04/11/2008
SSRS scripting with RS.EXE - 11/20/2007
SQL 2008 FILESTREAM - 08/04/2007
CTE Concatenation - 01/01/2007