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

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:01:00 UTC
 
0 Comments - Comment Feed - Permalink
Name
E mail (Private)
URL
Body
Human?
Tags:
.Net .net framework 4.0 ADO.NET AppleScript Astoria BI BeOS C C++ CAPTCHA Data Services EF GNOME GObject Groovy HTML Haiku JVM Java Lucene Mac MongoDB ORM Objective-C Operating Systems Oracle SSRS Solr VS 2010 Vala Web Services appengine c# clojure cloud clr cocoa touch concurrency couchdb cql cte curl database django dlr dynamic entity framework erlang exchange server filestream full-text functional go iPhone indexes ironpython ironruby jQuery linq lisp lucene monitoring natural language object oriented parallel performance podcasts powershell python rails refactoring remoting reporting services rs ruby scripting security setpolicies simpledb sql 2008 sql server stackless systems programming testing tools vb virtualization wave webdav windows xml