Wednesday, October 31, 2012

Best Practices for iOS Software Design


Best Practices for iOS Software Design

This article's goal is to help you write stable code for your iOS applications. I highly encourage you to contribute your own best practices via Github's pull requests.
Creative Commons License
This work is licensed under a Creative Commons Attribution 3.0 Unported License.
Originally written by: Jeff Verkoeyen (@featherless)

Table of Contents

Be Mindful of the Lifetime of Views

Remind yourself constantly that, at any time, your views may be destroyed.

Do not access self.view in init- methods

You should never access self.view in your controller's initialization methods. Doing so almost always leads to hard to debug bugs because that initialization logic will not be executed again after a memory warning.
Consider a simple example:
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
  if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) {
    self.view.backgroundColor = [UIColor underPageBackgroundColor];
  }
  return self;
}
Imagine this controller is the root of a navigation stack and a memory warning occurs. When we pop back to this controller, the view will no longer have underPageBackgroundColor as its background color. This leads to debugging frustration, even for experienced iOS engineers.

Use data source protocols to strongly separate data from views

When designing views that interact with data sets, always fetch the data via a data source protocol rather than exposing setters. Views are not data containers and should not be designed to enable any such abuse. Rather, views should be treated as expendable components that may leave existence at any point in time.
As a general rule of thumb, anything beyond static presentation information in a view should be requested via a data source or delegate.
UILabel is a good example of a view that does not need a data source. All of its properties are set once and are generally not expected to change throughout the lifetime of the view.
UITableView on the other hand requires a data source to fetch its data. Let's imagine what using UITableView would be like if it didn't have a data source and instead only provided setters.
This design will lead to inevitable abuse when developers attempt to use the table view object as a place to store their data. When the table view is inevitably released due to a memory warning the data will also be lost! This means that we need to store the data elsewhere anyway in order to guarantee its lifetime across multiple instances of the table view.

UIViewController

View controllers are the binding between your models and your views.

Use the existing navigation item object

Every instance of a UIViewController has a navigationItem property which should be used to specify the left and right navigation buttons and the title view. You should not create a UINavigationItem object because the base UIViewController implementation will automatically create one when you access self.navigationItem. Simply access self.navigationItem and set its properties accordingly.
// UIViewController will automatically create the navigationItem object.
self.navigationItem.rightBarButtonItem = doneButton;

NSObject

Only expose public properties and methods in headers

Objective-c allows you to define private properties in a category interface within your .m files; take advantage of this fact to provide better headers.
This is equivalent to defining ivars as @private with the added benefit of changes not causing build propagation when modifications are made to the internal structure of an object. This can be particularly helpful if an object is being refactored in a fairly large project.

Example

ViewController.h
@interface ViewController : UIViewController
@property (nonatomic, readonly, assign) NSInteger objectId;
@end
ViewController.m
#import "ViewController.h"

@interface ViewController()
@property (nonatomic, readwrite, assign) NSInteger objectId;
// Notice that this property doesn't need to be in the .h. Objective-C will create this
// property on the fly!
@property (nonatomic, readwrite, retain) UILabel* objectLabel;
@end

@implementation ViewController
@synthesize objectId;
@synthesize objectLabel;

...

@end

Debugging

Use lldb for debugging

lldb allows you to inspect properties on classes that don't have explicit ivars declared in the object's interface.
To use lldb, select "Edit Scheme..." from the "Product" menu (or press Cmd+Shift+<). Select the "Run" tab on the left-hand side of the scheme editor. Change the debugger drop down to "LLDB".

Use NSZombieEnabled to find object leaks

When NSZombieEnabled is enabled, objects that are released from memory will be kept around as "zombies". If you attempt to access the released object again in the future, rather than crashing with very little context, you will be shown the name of the object that was being accessed. This can be incredibly helpful in determining where memory leaks are occurring.
To turn on NSZombieEnabled, select "Edit Scheme..." from the "Product" menu (or press Cmd+Shift+<). Select the "Run" tab on the left-hand side of the scheme editor. Select the "Arguments" tab in that page. Add a new Environment Variable and call itNSZombieEnabled. Set its value to YES.

Documentation

Data Source Protocols

For required methods, start the preamble with "Tells the data source" and end it with "(required)". For example:
Tells the data source to return the number of rows in a given section of a table view. (required)
For optional methods, start the documentation with "Asks the data source". For example:
Asks the data source to return the number of pages in the launcher view.

Delegate Protocols

For methods that simply notify the delegate that an action has occurred, start the preamble with "Informs the delegate". For example:
Informs the delegate that the specified item on the specified page has been selected.

Bring Users Back to your Application after they End the Call


Often times, developers want to allow their users to place calls from inside their applications. Although, Apple has provided a pretty neat function openURL: for doing that, but it has one disadvantage…rather than bringing the users back to the application, it takes the users to the default Phone application after they end the call. It can be frustrating to both the developers and the users; developers want to give uninterrupted use of their applications whereas users hate to have to close the Phone app and go back to what they were doing. If you are one of the afore mentioned developers, you are reading just the post. In this small pots, Im showing a way around this…as always, totally acceptable by Apple, no private APIs or hacks that may get your app rejected.
No magic here, just a little observation; if you have ever made a call from Safari, you would have noticed that ending the call takes you back to Safari. Like me, you must have believed that Apple has added this custom behavior to Safari which is not accessible to us, bla bla bla…..actually thats not the case. This is UIWebView magic and if you use UIWebView to make calls instead ofopenURL:, you would get the same behavior. Lets see how to do that.

Using UIWebView to Place Calls

Simply, open your tel URL in a UIWebView. Like this
UIWebView *callWebview = [[UIWebView alloc] init];
NSURL *telURL = [NSURL URLWithString:@"tel:number to call"];
[callWebview loadRequest:[NSURLRequest requestWithURL:telURL]];


See, you dont even have to add the webview to your UI. Isn't this awesome?? Whats more awesome is, it displays an alert for confirmation before actually placing the call ;)
Feel free to leave a comment and wander around on my blog to find out more interesting stuff. You can also follow me here or on twitter (@xs2bush) to stay updated. Happy Coding :)
Update: The second line of code was missing a semi colon, thanks Ken for pointing that out. Also the UIWebView object was accesses using wrong variable name. All is correct now! I apologize for any inconvenience that it has might caused anyone :) I have tested it on my iPhone and it works perfectly.

An RSS-feed and location-based iOS application


An RSS-feed and location-based iOS application

The purpose of this post is so that I will have a link to give people when they ask: how do I write an iOS application that pulls data from an RSS feed, displays it pretty and can put things on a map. I'll show you all of that and more as I rewrite my oldest iOS application from scratch: FuelView.
The app presented in this post, FuelView, is freely available on the Australian App Store.

Introduction

I am commonly asked to write a post where I show a basic "pull data from the network and display" application.
But I think "basic" is boring and I try to avoid it in my blog posts. Instead, I decided to rewrite an application that looks simple but actually has a deceptively large amount of work to perform. It's a far better example of a real-world iOS application since there should always be enough work done transparently by your application that it leaves the user with a "Wow!"
The actual application is only 3 screens. While a truly simple three screen application might take about 2-3 hours per screen to implement (even if you're just stumbling your way through), this program actually took me about twice that time to implement (around 15 hours or two full days of programming) and I definitely knew what I was doing, since I've written the entire program before.
If you're curious though, in its first incarnation, this program took me 2 weeks to fully implement — way back in July 2008. It was the first program I ever tried to write for iPhone OS 2.0 and I did a lot of things the wrong way. Having a vague idea what you're trying to do really does make a 5 to 10 times difference to implementation time and unfortunately, the only way to learn is to stumble headlong into things and run the risk of getting everything wrong the first time.
There's a reason why this version is a rewrite from scratch.

Useful code in this post

While the application in this post is only directly useful to people living in Western Australia, I think this is a really interesting project as it contains a lot of very useful snippets of code (some of which I've written posts on before) including:
Plus a whole lot more. It really is a densely packed little program.
So where is all the "useful code"? If you skip forward to the second last section, I reveal where in the program you can find all of these code samples.

About FuelView

FuelView is an application for getting fuel (petrol/gasoline, diesel, etc) prices in Western Australia. It pulls its information from an RSS feed provided by the Western Australian government's "Fuel Watch" scheme that provides fuel prices for all stations in that state.
(No, I don't live in Western Australia — I'm on the opposite coast of Australia — but it seemed like a good idea for an iPhone app at the time.)
The application looks like this:
Fuelviewscreenshots
Download the complete project associated with this post: FuelView.zip (330kb).

Note: the code is all freely available under a zlib-style license but this license does notextend to the other assets. You may not use the icons or application name in your own programs.

The previous version of FuelView (1.1.10 is available for free from the iTunes App Store in Australia. I'll be resubmitting this version (probably 1.2) in a week or two.

Deceptive complexity

It all looks very simple; you get the location from the GPS, you pull the correct RSS feed for the location, stick pins in a map for the result.
If that were the actual number of steps involved, it would be great. However, it's not so simple. Let's take a quick look at the issues that will cause the most trouble for this program.
User location issues
The GPS gives latitude/longitude but the getting the RSS requires a Western Australian suburb name. In order to make this work, you need to be able to look up all the Western Australian suburbs and find the nearest one for your longitude/latitude. This requires a database of suburb names and their longitude/latitude and some code to search this.
Additionally, I want to be able to let the user specify a postcode for their location instead of using the GPS. Again, I need to be able to look up the suburb name for the postcode. Additionally, any code that requires the user's location must be able to tolerate the location being a postcode, not a raw longitude latitude.
A further complication arises because the FuelWatch RSS feed only exists for "larger" suburbs. Names of smaller suburbs can't be used, so the list of suburbs must be filtered to match the list that the FuelWatch website recognizes.
Station location issues
The RSS feed gives the fuel station locations as street addresses but I need longitude and latitudes for them so I can stick pins in the map or calculate the distance between the user and the station.
While this is a similar problem to resolving the user's location, it is actually trickier since new stations appear all the time, so this database must be dynamic (unlike the static postcode database).
I need to actually perform geocoding of fuel stations but there's a problem: Google's APIs are highly restrictive about how often you can make requests. The only way to avoid problems is to pre-populate the database and then have users only perform forward geocoding requests when an unknown fuel station appears and immediately add the new station to the database.
Custom drawing
Then I have the more straightforward complexity of custom drawing. I want to have most of the interface follow a custom aesthetic (because plain is boring) and that takes time and effort. I also need to ensure that drawing and layout function properly on an iPad and an iPhone.

The real design of the program

FuelViewDesignIn the diagram here, the green objects are the 3 main view controllers, the orange objects are the main "model" of the program (being the data that is actually displayed and passed between theResultsViewController and the MapViewController) and the blue objects are the data controllers or functional pipelines of the program.
You can see some of the "deceptive complexity" here: the ResultsViewController is managing three different kinds of model data, and is also managing input from the CLLocationManager andUITextField. All this is apart from the normal responsibility of showing and displaying theUITableView and its rows.

Initial design of the program

While I'd love to say: I had this entire design ready when I started, it's simply not true. What I actually had was a quick UI design and a quick sketch of the data pipeline.
My inital sketch for the UI looked like this:
FuelViewInterfaceDesignI didn't really need to do this sketch for this program (since the UI is pretty simple) but it's a good way to start all user programs.
FuelViewInitialDesignAs you can see, in the three years since I wrote FuelView, I had entirely forgotten about the need to resolve postcodes to suburb names and resolve station locations — instead I've simply shown the network pipeline.
This forgetfulness is pretty disappointing. Not only does it make me think the program is taking longer than it should but it ultimately leads to some of the design problems I discuss later.
But we're not there yet.

First implementation iteration

I'm going to refer to the design stages as "iterations". These steps are often called milestones (or mini-milestones when they're this small) but milestone implies you're only going forward, instead of the reality where you normally need to update the existing interfaces on your classes as part of integrating new features — so I prefer to think about it as iterating the program.
The first iteration involved implementing the program as described in the "FuelView Initial Dataflow design" shown above.
  1. Created a new project from my PageViewController template
  2. Pulled in an XMLFetcher
  3. Hardwired the code to pull the RSS feed for a specific suburb
  4. Displayed the list of addresses from the results as a basic text list
Screenshot4

Second design iteration

Now I need to be able to resolve locations from the CLLocationManager to suburb names. I also need to be able to resolve user-entered postcodes to actual suburb names. Finally, there needs to be logic so that a manually entered suburb name supercedes the GPS.
LocationsourcesAgain, I wouldn't ordinarily draw little design diagrams for this but — its a blog post and big walls of text are boring.

Second implementation iteration

To make the second design iteration work, I go through the following steps:
  1. Modified the CSVImporter from my Writing a parser using NSScanner post to generate a list of Western Australian postcodes with longitude and latitude. At the same time, I need to filter out suburbs that won't be recognized by the FuelWatch website
  2. Brought in a standard class for managing a Core Data NSManagedObjectContext and used the class to perform lookups on the list of Postcodes
  3. Added code to the ResultsViewController for CLLocationManager GPS locations
  4. Added code to the ResultsViewController to switch between the CLLocationManagerand user-entered Postcode data sources

Design Mistake #1

In a general sense, the program is "well-designed" but it still contains two design mistakes. I could fix these mistakes but (a) I'm lazy and (b) I wanted to talk about both of them since I think they're under-discussed design problems.
By this point in the implementation, the first mistake has emerged: the ambiguous overuse of the word "location".
Yes, ambiguous naming is a design mistake. It's not exactly an aspect of design that is thoroughly discussed — probably because it's considered self-evident — but as your program evolves and new functions, roles and elements are added to existing classes, it is sometimes necessary to change the names of the classes.
In this program, the word location has a few different meanings:
  • GPS location from the CLLocationManager (gpsLocation in the program)
  • User-specified postcode (usingManualLocation/postcodeValue in the program)
  • Resolved suburb name used for searching (location property on ResultsViewController — a Postcode object
  • The location of fuel stations (instances of the Location class)
Yuck, what a nightmare.
The single biggest mistake here is that the location property on the ResultsViewController is aPostcode object, despite the name implying the Location class. This is an ambiguity you should work really hard to avoid — and fix when it occurs.
A far better approach would actually be: rename the PostcodesController toSuburbsController, rename Postcode to Suburb, rename the suburb property on Suburb to nameand rename the location property to suburb. In addition, it would be better to rename Locationto Station.

Third implementation iteration

Now that the main data path is working, it's time to start on the custom views.
The GradientBackgroundTable will be the main UITableView class in the program. Its name is a bit of a misnomer: not so much of a misnomer that it's a design mistake but the table can draw as a gradient but can also draw as a flat color — it would be better named "Custom colored background table" or something like that.
Each result row will be represented using the following classes:
  • ResultCell — controller that constructs the rest of the row
  • ResultCellBackground — set as the backgroundView of the cell. Draws the gray gradient background and not much else
  • ResultView — draws all of the text (does not use UILabels) and uses the Gloss Gradient code to construct the backing for the actual price display
This now brings us a main screen that looks like this:
CustomdrawingNote that each row simply shows an orange "cents per litre" line under the price. Ultimately, this should show the distance from the user's current GPS location to the station but since I don't yet have the locations for the stations, I can only show "cents per litre". Note that this "cents per litre" will continue to display in the final program if the user is using a manually-entered postcode (and I don't necessarily know their GPS location).

Fourth implementation iteration

Now I need to resolve station locations. I have the street addresses from the FuelWatch RSS feed results but I need to turn this into longitude and latitude to calculate the distance or stick a pin in a map.
I'll use the Google APIs for this. As I've said though, the Google Maps API won't let you perform a large number of requests per second so I need to aggressively pre-cache station locations and only perform a geocoding request when a new fuel station appears for the first time.
The LocationsController and the Location lookup generally work like the Postcode lookup on the PostcodesController when the station is cached, otherwise, I need a callback when the actual response comes back from Google.
The LocationsController normally uses two Core Data data stores: one is read-only and is the "pre-cached" set of fuel station results (shipped with the application). The second is the read-write data store, saved in the Application Support directory (which is writeable, unlike the application's bundle). This second location will get all new locations for which we need to query Google.
To prepare the pre-cached file of stations that we can ship with the application, the best approach is simply to run in the iPhone Simulator with the LocationController's primary data store set to Read/Write (and the read-only store removed) and when the primary data store fills with results, let it save to file, copy the cached results file from the iPhone Simulator's directory back into the Project.
Once the location is resolved to a longitude and latitude, I can calculate the approximate distance to the user's GPS location, using the approximate kilometres per longitude and latitude at 30 degrees latitude (this is not highly accurate but is sufficient given that most of Western Australia is relatively close to 30 degrees latitude.
With the locations available, I can display the distance in the ResultsView and color code the distance bar based on how far away the station is.
Locationsavailable
Maps Key Note: I have removed my Google Maps API key from the code. If you want to use this code, you'll need to apply for your own Google Maps API key and set it the MapsKey at the top of the LocationsController.m file.

Fifth implementation iteration

Implementing the SettingsViewController to switch fuel types and the MapsViewController to show the current array of results on a map turns out to be very simple. There's not a significant amount of complexity in either of these views.

Design Mistake #2

One point to notice in the implementation of the MapViewController is that I needed to implement an "Adapter category" on NSDictionary to allow the NSDictionary to respond to the MKAnnotionprotocol so I could use the dictionaries to display the pins in the map.
How is this a design mistake?
Needing to put categories on generic classes like this is an indication that you probably should have used a dedicated class to contain your data. The results in the program should not be generic NSDictionary objects.
Until this point, the "generic"-ness of the main data type in the program has been ignored. The reality though is that the construction of NSDictionary results from XPathResultNodes and the resolving of station location for each result has been handled by the ResultsViewController — this is all work that a Result class should be performing for itself instead of using a genericNSDictionary class and making the ResultViewController handle all the work.
But ad hoc trickery like adding categories to generic container classes is a big flag that you've forgotten to use a custom class for objects that genuinely need their own behaviors. If you find yourself needing something like this: replace the generic container with a proper custom class.
I'm not saying that adapter categories are a bad idea. Sometimes you can't or shouldn't change the underlying class — in this case and adapter is a good thing. But here in FuelView I can change the underlying class and should, in order to reduce the code burden on our controllers.

So where is all the "useful code"?

An iOS version of my Gloss Gradient drawing codeThe GlossGradients.m code is in the project. It's very similar to the original code except that there aren't HSV conversion methods on UIColor like there is on NSColor, so I've had to write these methods myself. It is used in the ResultsView drawing code.
Two persistent stores with one NSPersistentStoreCoordinatorThe LocationsController uses two different persistent stores: a read-only store inside the application bundle that is shipped with the application contains the pre-supplied results for station lookups. But the application bundle can't be changed, so I create a read-write store in the Application Support directory. The NSPersistentStoreCoordinator is smart enough to save the store to the correct location automatically.
A full set of "single line Core Data fetch" methodsThe NSManagedObjectContext+FetchAdditions.m file contains a range of different fetch request creation methods and single line fetching implementations (for set, array and single object results). It is used in the LocationsController and the PostcodesController to perform the actual queries on the Core Data context.
Getting the GPS locationThe ResultsViewController operates as a CLLocationManagerDelegate. The location receiving code is pretty simple but I think the error handling code in locationFailedWithCode: is more interesting.
Pulling data from an RSS feedOf course, an RSS feed is just XML. We're after the <item> nodes in the result so I use anXMLFetcher to with an XPath query of "//item". You can see this in the setLocation: method and the response is handled in the responseReceived: method (the XPathQueryNodes are turned into an NSDictionary).
Caching data in the Application Support directoryThe application support directory is accessed/created in the persistentStoreCoordinatormethod of the LocationsController. As I said above, this is for writing extra Locations to the Locations Core Data context.
Function to create a two point CGGradientRef from two UIColorsThe function TwoPointGradient is pretty simple; it just creates a CGGradientRef taking twoUIColors to use as the endpoints of the gradient. However, it's 23 lines of code that don't need to be retyped in the ResultCellBackgroundResultView and the PageCellBackground.
An example of using a category as an Adapter interfacePutting an adapter on a generic container class is a bad idea if you can easily change the class to something of your own implementation. But this is still an example of adapting a class' interface to suit your own needs — something that is very useful when you don't have control over the underlying class.
Scrolling a text field that isn't in a tableThe manually entered postcode is in a text field on a UIToolbar and when the keyboard appears, the entire UIToolbar scrolls up with the keyboard. This behavior is handled by thePageViewController (everything below the "Handle the sliding/scrolling of the view when the keyboard appears" pragma except the dealloc method). The PageViewController needs to be set as the delegate of the UITextField for this to work.
A Core Data Postcode databaseThe PostcodesController shows how to implement a static data store using Core Data. I think I could probably write a base-class for this type of singleton that would dramatically reduce the common code between the PostcodesController and the LocationsController.
A flexible, reusable controller/table/cell structureThe PageViewControllerPageCell, all the view controllers and all the table cells are directly based on the code I presented in UITableView construction, drawing and management.
CheckmarkCell that self-manages radio button style selectionIt's strongly reliant on the PageViewController and PageCell classes but the CheckmarkCellshows an easy way (easy for the rest of the program) to manage a section in a table where only one row can be selected.
Forward geocoding using Google's Maps APIThe locationForAddress:receiver: method in LocationsController performs an XML request on Googles Maps API to forward geocode addresses into longitude and latitude (the response is handled in mapsResponseReceived:). Again: you'll need your own Maps API Key to make this work.

Conclusion

Download the complete project associated with this post: FuelView.zip (330kb).

Note: the code is all freely available under a zlib-style license but this license does notextend to the other assets. You may not use the icons or application name in your own programs.

The app presented in this post, FuelView, is freely available on the Australian App Store.
I did this rewrite of FuelView for three reasons:
  • I hadn't written any code at work for a week or two — I needed to scratch my codemonkey itch with a few hours of actual programming instead of meetings and documentation.
  • Preparing code like this for my blog motivates me to review my reusable classes and make them more presentable.
  • I'm often asked to show an iOS program that pulls data from an RSS feed.
The first two points are entirely for my own purposes and went fine, thanks.
On the third point — this is definitely code that pulls from an RSS feed. I hope the scale of the program doesn't make it hard to see how the basics work. In addition to RSS feed handling though, there's lot of other code here so I hope there's something here for programmers at a range of different skill levels.

Avoiding deadlocks and latency in libdispatch


Avoiding deadlocks and latency in libdispatch

The system-wide thread pool of libdispatch's global queue is an easy way to efficiently manage concurrent operations but it is not the solution to all threading problems and it is not without its own class of problems. In this post I look at deadlocks and latency problems that are inherent in thread-pool based solutions like libdispatch's global concurrent queue so that you will know when you should use this option and when you need something else.

Introduction: libdispatch's global queue is a constrained resource

In Mac OS X 10.6 (Snow Leopard), Apple introduced Grand Central Dispatch (GCD) for managing a system-wide thread pool. The normal API for GCD is libdispatch (despite the name, this API is part of libSystem).
The simplest way to interact with libdispatch is to execute blocks on the global queue:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{ /* perform some work */ });
For discrete packets of work that are not order dependent, this is a great solution. In this post, however, I'm going to look at situations where this is a poor choice.
The key limitation of this approach is that the global concurrent thread pool is a constrained resource — there are only as many active threads in this pool as you have CPUs on your computer.
Using this constrained resource indiscriminately can lead to the following problems once the limits of the resource are reached:
  • higher latency than other solutions
  • deadlocks for interdependent jobs in the queue

Latency problems

The following code simulates a tiny web sever or similar program that needs to serve a number of clients. In this case, it has 20 simultaneous requests from different clients.
In reality, this program just writes a small string to /dev/null 100,000 times for every client but that's sufficient to simulate any similar network or other non-CPU based operation.
int main(int argc, const char * argv[])
{
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_group_t group = dispatch_group_create();
     
    FILE *devNull = fopen("/dev/null", "a");
    const int NumConcurrentBlocks = 20;
    const int NumLoopsPerBlock = 100000;
    for (int i = 0; i < NumConcurrentBlocks; i++)
    {
        dispatch_group_async(group, queue, ^{
            NSLog(@"Started block %d", i);
            for (int j = 0; j < NumLoopsPerBlock; j++)
            {
                fprintf(devNull, "Write to /dev/null\n");
            }
            NSLog(@"Finished block %d", i);
        });
    }
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    dispatch_release(group);
    fclose(devNull);
     
    return 0;
}
This scenario illustrates the latency problems inherent in using the libdispatch global queue: on my computer, the first 4 blocks start immediately and the first of these finish 1.5 seconds later when the next blocks are started. The final block does not begin until 7.5 seconds after it is queued, finishing at the 9 second mark.
While the global queue in libdispatch is called "concurrent", it is only concurrent up to a threshold. Once the concurrent slots are full, the global queue becomes serial — in this case, the limit is reached and the serial nature increases the latency.
If this were a heavily loaded web server, instead of simply slowing down evenly for all users, the first few users would get a response and the last few would simply time out.
The solution to this type of problem is to create a specific queue for every block. Instead of pushing everything into the global queue (which is capped at 4 blocks on my test computer) we will have a separate queue for each block that will run simultaneously.
int main(int argc, const char * argv[])
{
    dispatch_group_t group = dispatch_group_create();
    FILE *devNull = fopen("/dev/null", "a");
    const int NumConcurrentBlocks = 20;
    dispatch_queue_t *queues = malloc(sizeof(dispatch_queue_t) * NumConcurrentBlocks);
    for (int q = 0; q < NumConcurrentBlocks; q++)
    {
        char label[20];
        sprintf(label, "Queue%d", q);
        queues[q] = dispatch_queue_create(label, NULL);
    }
    const int NumLoopsPerBlock = 100000;
    for (int i = 0; i < NumConcurrentBlocks; i++)
    {
        dispatch_group_async(group, queues[i], ^{
            NSLog(@"Started block %d", i);
            for (int j = 0; j < NumLoopsPerBlock; j++)
            {
                fprintf(devNull, "abcdefghijklmnopqrstuvwxyz\n");
            }
            NSLog(@"Finished block %d", i);
        });
    }
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    dispatch_release(group);
    fclose(devNull);
     
    return 0;
}
The result is that all 20 blocks are started simultaneously. All run at approximately the same speed and all finish at approximately the same time.
Alternative solution: as suggested by Keith in the comments, since these operations are I/O bound, not CPU bound, a better solution would be to use a file write source in the queue instead of standard operation queue blocks. File write sources are removed from the queue when they are blocked on I/O and this would allow all 20 sources to operate equitably in the global concurrent queue (or any other single queue).

Deadlocking

Deadlocking occurs when a first block stops and waits for a second to complete but the second can't proceed because it needs a resource that the first is holding.
In the following program, 20 parent blocks are queued (which will more than fill the constrained resource of the global concurrent queue). Each of these blocks spawns a child block which is queued in the same global concurrent queue. The parent runs a busy wait loop until the child adds its own integer to the completedSubblocks set.
NSMutableSet *completedSubblocks;
NSLock *subblocksLock;
int main (int argc, const char * argv[])
{
    completedSubblocks = [[NSMutableSet alloc] init];
    subblocksLock = [[NSLock alloc] init];
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_group_t group = dispatch_group_create();
         
    const int NumConcurrentBlocks = 20;
    for (int i = 0; i < NumConcurrentBlocks; i++)
    {
        dispatch_group_async(group, queue, ^{
            NSLog(@"Starting parent block %d", i);
             
            NSDate *endDate = [NSDate dateWithTimeIntervalSinceNow:1.0];
            while ([(NSDate *)[NSDate date] compare:endDate] == NSOrderedAscending)
            {
                // Busy wait for 1 second to let the queue fill
            }
             
            dispatch_async(queue, ^{
                NSLog(@"Starting child block %d", i);
                [subblocksLock lock];
                [completedSubblocks addObject:[NSNumber numberWithInt:i]];
                [subblocksLock unlock];
                 
                NSLog(@"Finished child block %d", i);
            });
            BOOL complete = NO;
            while (!complete)
            {
                [subblocksLock lock];
                if ([completedSubblocks containsObject:[NSNumber numberWithInt:i]])
                {
                    complete = YES;
                }
                [subblocksLock unlock];
            }
            NSLog(@"Finished parent block %d", i);
        });
    }
     
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    dispatch_release(group);
     
    [completedSubblocks release];
    [subblocksLock release];
    return 0;
}
On my computer the first 8 blocks are started (filling the global queue's concurrent blocks) and these blocks block the global queue so that the child blocks can never run. Since the parent is waiting for the child and the child can never run, the result is a deadlock: the program never completes.
You'll notice that I've used a busy wait loop for both the 1 second delay and to detect when the child has completed. Normally, you would dispatch the child using dispatch_sync (which is a different way to wait for the child to complete) but the reality is that libdispatch is smart enough to remove a block from its queue while it is using one of the libdispatch wait mechanisms (or one of the many Cocoa or OS functions that similarly yields CPU time like sleep).
While using the correct functions to wait will fix this trivial example, it will not fix the situation where the processes might be genuinely busy.
The best solution is to avoid dependencies between blocks in the same queue.
In a situation like this where one of the blocks (the child block) consumes a trivial amount of time and the other is time consuming (the parent always takes at least 1 second), you can simply enqueue the trivial time block in a separate serial queue. That will prevent deadlocking in all cases.
In a situation where both parent and child are time consuming, you could try:
  • create a separate queue for every invocation of either the parent or child and queue the other in the global queue
  • roll the functionality of the child into the parent so that they are always done as a single block
  • carefully write your code to ensure that a libdispatch wait mechanism is always ahead of any dependency

Conclusion

While libdispatch does an excellent job at making concurrent, multithreaded programming easier, it does not magically eliminate some of the pitfalls.
The problems I've looked at in this post are mostly a result of the fact that queues are shared resources and there are situations where you must be careful about how resources are shared.
In that sense, these problems are not directly related to libdispatch (although I've used libdispatch to implement the scenarios) they are problems that can be caused by misusing any system where a fixed number of servers is pulling data out of a queue.
Grand Central Dispatch's global queue is intended for CPU intensive operations. That it limits concurrency to the number of CPUs in your computer is good for CPU intensive operations; it prevents wasting CPU time due to task swapping. The examples here demonstrate the downside: it may increase latency for queue blocks and you need to be careful about dependencies between blocks on the same queue.