CocoaConf Columbus 2012 Slides and Code

Yesterday I gave a talk on concurrency in OS X and iOS at CocoaConf Columbus. As promised, here are the slides and code:

Enjoy!

Asynchronous Synchronous Requests: Effortless Networking Code

Today I showed a couple of people at work a technique I use to do asynchronous URL loading in iOS, and the response on Twitter was great, so I’ve written it up for everybody. If you’re used to using ASIHTTPRequest or rolling your own NSURLConnection delegates, hopefully this method will be a breath of fresh air.

The Problem: When you want to load something from the Internet, you don’t want to block your UI—especially when iOS might just kill your app for doing so—but writing delegate code is a pain. You have to remember which delegate methods get called in what order, to set yourself as the delegate (can’t tell you how many times that’s tripped me up), and handling multiple simultaneous connections with one delegate is… tricky, at best.

The Solution: Use Grand Central Dispatch. Maybe I just love GCD too much and this is me seeing everything as a nail, but let’s look at the following code for loading a URL:

- (void)loadAwesomeURL
{
    NSString *awesomeURI = @"http://www.awesomeexample.com/?output=JSON";
    NSURL *awesomeURL = [NSURL URLWithString:awesomeURI];
    NSURLRequest *awesomeRequest = [NSURLRequest requestWithURL:awesomeURL];

    NSURLConnection *theConnection = [[NSURLConnection alloc] initWithRequest:awesomeRequest
                                                                     delegate:self];

    [theConnection start];
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
    [myMutableData appendData:data];
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    [self processTheAwesomeness];
}

That sucks. Three methods, and I didn’t even do any error handling! There has to be a better way. NSURLConnection offers a synchronous method, but everybody knows you don’t use it… so let’s do exactly that. But since we want to make this asynchronous, we’ll use Grand Central Dispatch to wrap it in a dispatch_async() call:

- (void)loadAwesomeURL
{
    NSString *awesomeURI = @"http://www.awesomeexample.com/?output=JSON";
    NSURL *awesomeURL = [NSURL URLWithString:awesomeURI];
    NSURLRequest *awesomeRequest = [NSURLRequest requestWithURL:awesomeURL];

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0ul);
    dispatch_async(queue, ^{
        NSURLResponse *response = nil;
        NSError *error = nil;

        NSData *receivedData = [NSURLConnection sendSynchronousRequest:awesomeRequest
                                                     returningResponse:&response
                                                                 error:&error];

        [self processTheAwesomeness];
    });
}

We can easily do error checking after the NSURLConnection call; simply check to see if receivedData is nil, cast response to an NSHTTPURLRequest and check its statusCode property, and if all else fails, check out error.

Note: I’ve received a fair amount of negative feedback on this article on Twitter, Reddit, and in the comments, and I feel like I ought to make a few points clear:

  • This is not the last networking solution you’ll ever need. Among other things, this does not support:
    1. Canceling the connection
    2. Running code when the connection is half-done
    3. Streaming data to a file for large downloads
  • This is a quick example. It’s mainly designed to illustrate dispatch_async() as a wrapper for synchronous APIs.
  • It isn’t good for multiple connections. You’ll want a custom dispatch queue for that.
  • It doesn’t run on the main thread. If you’re updating your UI, you’ll need to do that on the main thread.

Cocoa Touch Tutorial: Using Grand Central Dispatch for Asynchronous Table View Cells

One of the problems that an iOS developer will often face is the performance of table view cells. Table view cells are loaded on-demand by the UITableView that they’re a part of; the system calls ‑cellForRowAtIndexPath: on the table view’s dataSource property to fetch a new cell in order to display it. Since this method is called (several times) while scrolling a table view, it needs to be very performant. You don’t have very much time to provide the system with a table view cell; take too long, and the application will appear to stutter to your users. This kills the immersion of your application and is an instant sign to users that the application is poorly-written. I guess what I’m saying is that this code needs to be fast. But what if something you need to do to display the table view cell takes a long time—say, loading an image?

In my MobiDevDay presentation a couple of weeks ago, I illustrated a solution to this problem: Grand Central Dispatch. GCD, Apple’s new multiprocessing API in Mac OS X Snow Leopard and iOS 4, is the perfect solution for this problem. Let’s take a look at how it works.

Grand Central Dispatch operates using queues. Queues are a C typedef: dispatch_queue_t. To get a new global queue, we call dispatch_get_global_queue(), which takes two arguments: a long for priority and an unsigned long for options, which is unused, so we’ll pass 0ul. Here’s how we get a high-priority queue:

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);

It’s pretty straightforward. To use this queue, we add blocks of code onto it. Typically this is done with blocks (Apple’s new code encapsulation extension to the C language), though it can be done with C functions. To submit a block onto a queue for execution, use the functions dispatch_sync and dispatch_async. They both take a queue and a block as parameters. dispatch_async returns immediately, running the block asynchronously, while dispatch_sync blocks execution until the provided block returns (though you cannot use its return value). Here’s how we schedule some code onto a queue (we’ll assume this code runs after our previous example, so queue is already defined):

dispatch_async(queue, ^{
    NSLog(@"Hello, World!");
});

It’s very easy to forget the ); at the end of that line, so be careful.

How does this apply to table view cells? Let’s take a look at a typical scenario for loading images from disk:

- (UITableViewCell *)tableView:(UITableView *)tableView
         cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"ExampleCell";
    
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
                                       reuseIdentifier:CellIdentifier] autorelease];
    }
    
    // Get the filename to load.
    NSString *imageFilename = [imageArray objectAtIndex:[indexPath row]];
    NSString *imagePath = [imageFolder stringByAppendingPathComponent:imageFilename];
    
    [[cell textLabel] setText:imageFilename];
    UIImage *image = [UIImage imageWithContentsOfFile:imagePath];
    [[cell imageView] setImage:image];

    return cell;
}

The problem with that code is that creating image blocks until ‑imageWithContentsOfFile: returns. If the images are especially large, this is catastrophic. Modifying this code to use Grand Central Dispatch is simple:

- (UITableViewCell *)tableView:(UITableView *)tableView
         cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";
    
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
                                       reuseIdentifier:CellIdentifier] autorelease];
    }
    
    // Get the filename to load.
    NSString *imageFilename = [imageArray objectAtIndex:[indexPath row]];
    NSString *imagePath = [imageFolder stringByAppendingPathComponent:imageFilename];
    
    [[cell textLabel] setText:imageFilename];
    
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);

    dispatch_async(queue, ^{
        UIImage *image = [UIImage imageWithContentsOfFile:imagePath];
    
        dispatch_sync(dispatch_get_main_queue(), ^{
            [[cell imageView] setImage:image];
            [cell setNeedsLayout];
        });
    });
    
    return cell;
}

First, we create our image asynchronously by using dispatch_async(). Once we have it, however, we have to come back to the main thread in order to update our table view cell’s UI (all UI updates should be on the main thread, unless you like reading crash reports). GCD has a function to get the main queue—analogous to the main thread—called dispatch_get_main_queue(). We can dispatch a block to that thread to update the UI.

By making this simple modification, we can very easily improve the performance of our table view. There are a few steps remaining, however, and this method has one serious shortcoming: if the cell is re-used by the time the image loads, it can load the wrong image into the cell. To get around this, it would be better to cache the images in an array or a dictionary (just be sure to release it in your view controller’s ‑didReceiveMemoryWarning: method). That said, this is an example of something you can do quite easily to improve the performance of your application. The better it performs, the more your users will like it, and that’s the ultimate goal.

The code used in this post is available as a GitHub repository.

iPhone Programming: Hard Mode

After a conversation with some co-workers, I discussed how it would be technically possible to write an iPhone app using only a main.m file—no separate class files. This post is the result of that. It’s definitely doable, but not something I’d ever recommend for shipping code.
The code is explained below, but you can get the full source in my public GitHub repository.

Getting Started

By default, an iPhone application template gives you a main.m file, but it’s pretty basic:

#import <UIKit/UIKit.h>

int main(int argc, char *argv[])
{
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    int retVal = UIApplicationMain(argc, argv, nil, nil);
    [pool release];
    return retVal;
}

To go from that code to running your application, a few things happen, mostly in UIApplicationMain. Its last argument, typically nil, is a pointer to an NSString that specifies the class name of the application delegate. Normally, your Info.plist file specifies a nib file (the keys NSMainNibFile or NSMainNibFile~ipad), which in turn specifies the application delegate, instantiates it, etc.

So, first, delete the MainWindow.xib file, then all other class files (MyCoolAppDelegate.h and MyCoolAppDelegate.m, for instance). There should be three files left in the iPhone application: your Prefix.pch file (which we could technically do without), main.m, and your Info.plist file.

The Fun Begins

Administrivia: Creating Strings

Since, as a part of the exercise, we don’t want to use Objective-C, I also decided against using any constant NSStrings (@"Hello, World!" and the like). To create a string, then, we’ll use CFStrings. I created a macro to create one from a C string using ASCII encoding:

#define JK_EasyCFString(X)
(CFStringCreateWithCString(kCFAllocatorDefault, (X), kCFStringEncodingASCII))

Don’t forget to call CFRelease() when you’re done with it, though.

Creating a Class

We need to modify the call to UIApplicationMain to include our application delegate class if we want this app to be anything more than a blank screen. What class name can we give it, though? We’ll need to create a class, and we’ll call it “HardModeAppDelegate.”

	// Create a class to serve as our application delegate.
	Class appDelegate = objc_allocateClassPair(NSClassFromString((id)NSObjectString), "HardModeAppDelegate", 0);

The first argument is a toll-free bridged CFString that specifies NSObject as the superclass, then we pass in the name of the class as a C string and 0 for the number of extra bytes we want (which is nearly always zero). Now let’s set it up a bit:

	// Conform to the UIApplciationDelegate protocol.
	Protocol *appDelegateProto = NSProtocolFromString((id)UIApplicationDelegateString);
	class_addProtocol(appDelegate, appDelegateProto);

	// Add methods.
	SEL applicationDidFinishLaunchingWithOptions = NSSelectorFromString((id)appDidFinishLaunchingOptionsString);
	class_addMethod(appDelegate,
					applicationDidFinishLaunchingWithOptions,
					(IMP)UIApplicationDelegate_ApplicationDidFinishLaunchingWithOptions,
					"v@:@@");

	SEL dealloc = NSSelectorFromString((id)deallocString);
	class_addMethod(appDelegate,
					dealloc,
					(IMP)appDelegate_dealloc,
					"v@:");

	// Add an instance variable for the window.
	class_addIvar(appDelegate,
				  "window",
				  sizeof(id),
				  log2(sizeof(id)),
				  "@");

	// Now that we’ve added ivars, we can register the class.
	objc_registerClassPair(appDelegate);

That code is the same as writing an @interface block for a new class, setting up ivars, conforming to protocols, and defining instance methods, but done using the runtime calls instead. Methods are actually C functions, but with two arguments prepended to the argument list: id self and SEL _cmd. Those arguments are actually passed to every Objective-C method, but usually hidden.

Custom Message Sending

The rest of the application is more of this bootstrapped Objective-C in C, but there are a few wrinkles worth discussing, most notably the use of objc_msgSend with methods that return other than id and/or have additional arguments beyond self and _cmd. For instance, to call -bounds on a UIScreen object, I had to cast the return type of objc_msgSend to CGRect:

CGRect (*msgSendBounds)(id self, SEL _cmd);
  msgSendBounds = (CGRect(*)(id, SEL))objc_msgSend_stret;

Similarly, when calling -initWithFrame: on a UIWindow or UILabel, I casted it to take in a CGRect argument:

id (*msgSendCGRect)(id self, SEL _cmd, CGRect rect);
msgSendCGRect = (id(*)(id, SEL, CGRect))objc_msgSend;

Accessing Instance Variables

In my implementation of the HardModeAppDelegate class’s -dealloc method, I need to access the window instance variable to send it a -release message. Using the Ivar type and the object_getIvar function, it becomes easy:

Ivar windowIvar = class_getInstanceVariable(self->isa, "window");
id window = object_getIvar(self, windowIvar);

So what do we have?

To be clear, this is not writing an iPhone app without using Objective-C, per se. The runtime is still being used, messages are being sent, all that. But it is an illustration of some of the heavy lifting that the runtime does for you. I would caution against adopting this practice for real, shipping apps.

Quick Tip: Don’t Do This

I could not find out where a bug was coming from for the life of me today. Naturally it one of those “assignment instead of equality” bugs that seem to crop up when trying to code too quickly. The difference here was that I had implemented a subclass of NSObject that reimplemented the method +resolveInstanceMethod:. So, the code went like this:

+ ( BOOL )resolveInstanceMethod:( SEL )sel
{
    if ( sel = @selector( setFoobar: )) {
        return class_addMethod([ self class ], sel, ( IMP )setFB, "v@:@" );
    } else if ( sel = @selector( foobar )) {
        return class_addMethod([ self class ], sel, ( IMP )getFB, "@@:" );
    } else {
        return [ super resolveInstanceMethod:sel ];
    }
}


For those of you keeping score at home, when the Objective-C runtime tried to resolve any selector, this method happily added a selector for -setFoobar: to self’s class and returned YES.

Don’t do this.