Even more Cocoaheads Presentations

I’ve continued my journey with Final Cut Pro X and created more videos from talks done at the June Melbourne Cocoaheads meetup. These videos are now up on the Melbourne Cocoaheads Vimeo group (and embedded below).

David Kennedy & Scott Manley of Dangerous Pixels speak about building their iOS app development consultancy and their application Task Caddy.

You can find a wrap-up from the Dangerous Pixel guys, with links, slides and other resources over on their blog.

Luke Cunningham & Jesse Collis on “Epic Refactorings and Patterns to Make Your Code Awesome”.

What’s wrong with this macro?

What is wrong with this Objective-C (and C/C++) macro?


#define RETAIN_PROPERTY(propertyName, newValue) \
    do { \
        if (propertyName##_ != newValue) \
        { \
            [newValue retain]; \
            [propertyName##_ release]; \
            propertyName##_ = newValue; \
        } \
    } while(0)

Looks pretty benign right? Well it is when you call it like this:


id n = [NSNumber numberWithInt:10];
RETAIN_PROPERTY(propName, n);

But what happens when you do this:


RETAIN_PROPERTY(propName, [NSNumber numberWIthInt:10]);

Well macros are just that, macros. They generate code. They substitute the text of the macro parameters and emit the body into your source. Unlike methods or functions, they do not evaluate their parameters before passing the result to the macro. So the macro above when called the second way will produce the following code:


    do {
        if (propName_ != [NSNumber numberWIthInt:10])
        {
            [[NSNumber numberWIthInt:10] retain];
            [propName_ release];
            propName_ = [NSNumber numberWIthInt:10];
        }
    } while(0);

Not quite what we wanted right. We’re leaking an NSNumber instance and creating two which are auto-released and we’ll probably have a zombie object stored in propName_ after the auto-release pool is drained. A nice confusing bug for us to stumble upon.

So how do we fix this?

Like so:


#define RETAIN_PROPERTY(propertyName, newValue) \
    do { \
        __typeof__(newValue) __A = (newValue); \
        if (propertyName##_ != __A) \
        { \
            [__A retain]; \
            [propertyName##_ release]; \
            propertyName##_ = __A; \
        } \
    } while(0)

The line __typeof__(newValue) __A = (newValue); forces the passed parameter to be evaluated and a result stored in the temporary __A. We can then use the temporary variable multiple times within the macro body without fear.

Bug fixed and we can get back to being productive coders.

Cancellable asynchronous searching with UISearchDisplayController

Apple’s UISearchDisplayController is a handy wrapper around searching for UITableViewControllers. UISearchDisplayController will send its delegate the following message whenever a user types something into the search bar:

 - (BOOL)searchDisplayController:(UISearchDisplayController *)controller 
shouldReloadTableForSearchString:(NSString *)searchString

It is in this method that the delegate should perform a search of its data and based on whether the search results have or would change return the value YES or NO.

This all works fine and dandy when the objects to search are in memory and the delegate method executs promptly. If this is not the case then the UI will block while the search logic is performed. This can create significant lag in the user interface as the user is typing into the search field.

In the iOS API documentation for searchDisplayController:shouldReloadTableForSearchString: Apple provides a hint about performing search work in the background but doesn’t really go into any detail. The docs say:

You might implement this method if you want to perform an asynchronous search. You would initiate the search in this method, then return NO. You would reload the table when you have results.

An approach to doing an asynchronous search might be something like this:

@interface MyUISearchDisplayDelegate ()
@property (nonatomic, retain) NSOperationQueue *searchQueue;
@property (nonatomic, retain) NSArray *searchResults;
@end

@implementation MyUISearchDisplayDelegate
@synthesize searchQueue, searchResults;

- (id)init
{
    if ((self = [super init]))
    {
        self.searchResults = [NSMutableArray array];
        self.searchQueue = [[NSOperationQueue new] autorelease];
        [self.searchQueue setMaxConcurrentOperationCount:1];
    }
    
    return self;
}

- (void)dealloc
{
    self.searchResults = nil;
    self.searchQueue = nil;
    
    [super dealloc];
}

// ...

 - (BOOL)searchDisplayController:(UISearchDisplayController *)controller
shouldReloadTableForSearchString:(NSString *)searchString
{
   if  ([searchString isEqualToString:@""] == NO)
    {
        [self.searchQueue addOperationWithBlock:^{
            
            NSArray *results = // fetch the results from 
                // somewhere (that can take a while to do)
            
            // Ensure you're assigning to a local variable here.
            // Do not assign to a member variable.  You will get
            // occasional thread race condition related crashes 
            // if you do.            

            [[NSOperationQueue mainQueue] addOperationWithBlock:^{
                // Modify your instance variables here on the main
                // UI thread.
                [self.searchResults removeAllObjects]; 
                [self.searchResults addObjectsFromArray:results];
                
                // Reload your search results table data.
                [controller.searchResultsTableView reloadData];
            }];
        }];
        
        return NO;
    }
    else
    {
        [self.searchResults removeAllObjects];
        return YES;
    }
}

This implementation gets you some of the way there. The problem with it however is that as the user interacts with the UISearchBar the searchString changes and with each character typed another invocation of searchDisplayController:shouldReloadTableForSearchString: is performed. This causes lots of work to be enqueued into the NSOperationQueue. Work that must get executed in sequence.

This is not ideal. The user is only interested in the most recent search string that they have entered. If they enter the string “fubar” into the UISearchBar they don’t care about the searches for f, fu, fub, and fuba.

OK. So how do you properly implement this? Well, it is surprisingly easy. Just cancel all the existing operations in the NSOperationQueue before adding your latest search operation to the queue.

     [self.searchQueue cancelAllOperations];

Any yet to execute operations will be cancelled and never execute. Any presently executing operation will run to completion. In a more complex situation, perhaps with a multistep NSOperation, (where you were using a custom subclass of NSOperation or something) you could check for isCanceled on NSOperaton instance and bail out of your operation early. I’ll leave that up to you to figure out.

So, with this change the code becomes:

 - (BOOL)searchDisplayController:(UISearchDisplayController *)controller 
shouldReloadTableForSearchString:(NSString *)searchString
{
   if  ([searchString isEqualToString:@""] == NO)
    {
        [self.searchQueue cancelAllOperations];
        [self.searchQueue addOperationWithBlock:^{
            
            NSArray *results = // fetch the results from
              // somewhere (that can take a while to do)
            
            // Ensure you're assigning to a local variable here.
            // Do not assign to a member variable.  You will get
            // occasional thread race condition related crashes 
            // if you do.            

            [[NSOperationQueue mainQueue] addOperationWithBlock:^{
                // Modify your instance variables here on the main
                // UI thread.
                [self.searchResults removeAllObjects]; 
                [self.searchResults addObjectsFromArray:results];
                
                // Reload your search results table data.
                [controller.searchResultsTableView reloadData];
            }];
        }];
        
        return NO;
    }
    else
    {
        [self.searchResults removeAllObjects];
        return YES;
    }
}

One final little thing, it is probably a good idea to cancel any pending operations when the user dismisses the search UI.

- (void)searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller
{
    [self.searchQueue cancelAllOperations];
}

You may wish to cancel any pending operations in other delegate methods too. Such as tableView:didSelectRowAtIndexPath: for example.

Using p4merge as a custom Git merge tool on Mac OS X.

On Mac OS X Git uses the opendiff(1) command line utility as its merge tool by default.

opendiff(1) will launch the FileMerge app that comes with XCode when performing merges. Unfortunately FileMerge is not a very good merge tool.

To improve this, there are a number of options. There are some good commercial, Open Source and freeware diff tools out there for Mac OS X such as DiffMerge, SureMerge, Araxis Merge, K3Diff, and p4merge, etc. Too many to list them all here.

For my system I chose p4merge. p4merge is part of the Perforce source control management system. I’ve used Perforce quite a lot in the past at previous jobs and it has great tools. Perforce is commercial software, and I’m not about to use Perforce as my source control system instead of Git, but fortunately you can download and use the Perforce GUI tools for free. The Perforce merge tool is not tied in any way to the Perforce SCM system so you can use it as a stand alone tool.

To set up p4merge as my mergetool with Git I did the following:

  1. Downloaded the Perforce Visual Client disk image from Perforce.com
  2. Installed p4merge into /Applications.
  3. Added the these settings to my ~/.gitconfig file.

Now git mergetool will launch p4merge as my preferred merge tool.

p4merge Screenshot

Now you may be wondering why I didn’t just set my Git merge.tool config setting to p4merge, as it is supported “by default” by Git. Well, Git expects the command p4merge to be in the $PATH and I’d rather not have to install shell scripts across the different Mac systems I use so that it works “out of the box”. I also found p4merge wasn’t dealing well with the relative paths that Git was trying to pass to it, hence the custom mergetool.custom.cmd setting that uses $PWD.

Foggy CarrierWave Nokogiri woes on Mac OS X Snow Leopard

I’m developing a Ruby on Rails app on my Mac running Snow Leopard. Tonight I wanted to use the fog gem with CarrierWave to upload files to Amazon S3.

Fog is a Ruby wrapper around different cloud services and CarrierWave provides a very nice API for handling file uploads in Rails apps.

Fog has a dependency on the Nokogiri HTML/XML parsing library, which has a dependency on libxml2, libxslt, and libiconv.

Welcome to pain and suffering…

The versions of libxml2 and libxslt on MacOS X are a little out of date. And when trying to install the nokogiri gem with:

sudo gem install nokogiri

You will get an error like this:


ERROR: Error installing nokogiri:
ERROR: Failed to build gem native extension.
...
checking for libxml/parser.h... yes
checking for libxslt/xslt.h... yes
checking for libexslt/exslt.h... yes
checking for iconv_open() in iconv.h... no
checking for iconv_open() in -liconv... no
-----
libiconv is missing.
please visit http://nokogiri.org/tutorials/installing_nokogiri.html
for help with installing dependencies.
-----

Now, libiconv isn’t missing. It is installed with MacOS X. But nokogiri’s build script can’t find it for some reason.

The Nokogiri install instructions suggest installing newer versions of libxml2 and libxslt to solve this problem. They even kindly provide instructions for a number of operating systems including MacOS X.

Unfortunately none of them worked for me.

I’m using homebrew to manage my additional source packages on my Mac. Installing libxml2 via homebrew is easy:

brew install libxml2

Installing libxslt, not so much.

There is no brew formula for libxslt in the default homebrew git repo. I have no idea why. Perhaps something to do with homebrew’s policy not to dupe stuff bundled with MacOS X. Not sure why they provide a libxml2 forumla then as it comes with MacOS X too.

So to install libxslt I did the following:


curl ftp://xmlsoft.org/libxml2/libxslt-1.1.26.tar.gz -o libxslt-1.1.26.tar.gz
tar zxfv libxslt-1.1.26.tar.gz
cd libxslt-1.1.26
./configure \
--prefix=/usr/local/Cellar/libxslt/1.1.26 \
--with-libxml-prefix=/usr/local/Cellar/libxml2/2.7.7
make
make install
cd .. && rm -rf libxslt-1.1.26 && rm -f libxslt-1.1.26.tar.gz

To install nokogiri I then used the following command line.


sudo gem install nokogiri -- \
--with-xslt-dir=/usr/local/Cellar/libxslt/1.1.26 \
--with-xml2-include=/usr/local/Cellar/libxml2/2.7.7/include/libxml2 \
--with-xml2-lib=/usr/local/Cellar/libxml2/2.7.7/lib \
--with-iconv-dir=/usr

Now everything seems to be working ok.