Using blocks as a flexible callback mechanism
September 13, 2009
Among the many numerous developer improvements in Snow Leopard is the addition of blocks to the Objective-C language (now at version 2.1). While working on some networking code using
NSURLConnection I discovered that blocks provide a beautiful and flexible mechanism for implementing callbacks.
As a quick example, let’s say you want to initiate two GET requests to different websites. Perhaps the first is to post some form data and the other is just to download the website content. No matter the particular specifics, the idea is that for different types of
NSURLConnection requests you will want to respond differently upon completion. For the first you might want to update the UI to show that the form data was posted. For the second you may want to display the site in a web view. To accomplish this you would traditionally need to keep an array around to hold not only the
NSURLConnection instances themselves, but some kind of associated identifier for each one. Otherwise, when your delegate methods fire you will have no idea which action initiated the request in the first place — and thusly you wouldn’t necessarily know what to do next after the connection is finished loading.
The following is a bit of untested pseudo-code (a rough approximation if you will) of one of the traditional methods of handling this:
In short, you will have to have a bunch of conditional if-else statements in order to properly respond to each different connection’s completion. The alternative approach is to write a
URLConnectionManager class based on blocks:
This code also takes advantage of associated objects, another new language feature that allows us to avoid having to keep an array of connection info dictionaries around to store the associated
responseBlock reference. (I am using an elegant category wrapper of the C API written by Andy Matuschak.)
Using this approach, the two connection requests can be implemented as follows:
Note that there are no more if-else statements to deal with and even though the requests are being performed asynchronously, you can write a piece of code to deal with the response in the same spot that you make the initial connection invocation. This results in a much cleaner and more readable codebase.