Monthly Archives: December 2010

Missions of the Reliant: Broadcasting distress call.

At the suggestion of Jude, seconded by none other than the man himself, I’ve created a mailing list for announcements and discussions about Missions of the Reliant.

Unfortunately, I’m a bit of a newbie when it comes to mailing lists, and I had to settle for a Google Group for simplicity’s sake: http://groups.google.com/group/missions-of-the-reliant. It’s members-only, but anyone can subscribe. Please feel free to sign up and comment; it’s open for discussion as well as news :-).

A handy trick with blocks and callbacks in Objective-C

While writing some code to implement a block-based callback on top of some classes which are still stuck in the target-action paradigm, I stumbled across this rather nice little trick for doing so in a category without having to do any memory management tricks at all in retain-release mode. This example comes from adding a block callback to iOS’s UIBarButtonItem (DR is for Dark Rainfall, of course):

// This category is required, but doesn't have to be in the same place.
// The concept is simple, and used by several other code libraries: Because blocks are
// also objects when the Objective-C runtime is loaded, a category on NSObject adds
// the selector to them. Obviously it would be foolish to call this category on an object
// that was not a block.
@interface NSObject (DRBlockCallback)
- (void)DRcallbackBlock;
- (void)DRcallbackBlockWithSender:(id)sender;
@end

@implementation NSObject (DRBlockCallback)
- (void)DRcallbackBlock { ((void (^)())self)(); }
- (void)DRcallbackBlockWithSender:(id)sender { ((void (^)(id))self)(sender); }
@end
// Now for adding a block callback...
@interface UIBarButtonItem (DRButtonBlockCallback)
- (id)initWithBarButtonSystemItem:(UIBarButtonSystemItem)item block:(void (^)(id))b;
@end

@implementation UIBarButtonItem (DRButtonBlockCallback)
- (id)initWithBarButtonSystemItem:(UIBarButtonSystemItem)item block:(void (^)(id))b
{
    // Call correct initializer. Provide the block callback as action, but no target yet
    if ((self = [self initWithBarButtonSystemItem:item target:nil action:@selector(DRcallbackBlockWithSender:)]))
    {
        // It's necessary to copy the block and hang on to it somewhere. Categories can not add
        // ivars to a class. Associated references were designed to solve exactly this kind of
        // problem. This call associates the block with this object, conveniently doing the needed
        // copy as well.
        objc_setAssociatedObject(self, "DRactionBlock", b, OBJC_ASSOCIATION_COPY_NONATOMIC);
        // Now when the block is retrieved back from the association, the copy is returned, which is
        // the target for the block callback action.
        self.target = objc_getAssociatedObject(self, "DRactionBlock");
    }
    return self;
}

// There is no need to override dealloc. The association will automatically be dissolved when the
// object is released, at which point the copied block will also be released.
@end

Poof. Proper management of a block’s memory in a retain-release environment without any effort! This little trick could be further simplified by another method on NSObject, something like - (id)associateBlock:(void (^)(id))b withKey:(const char *)key;, but that’s overkill in my opinion. For completeness’ sake, I also wrapped the whole thing in #if NS_BLOCKS_AVAILABLE, though on iOS that’s probably unnecessary, as I have no intention of ever developing for <4.0 again, and even on OS X I would use PLBlocks if I had to target Leopard.

For those waiting for Missions of the Reliant news, I beg your patience. I have not forgotten!