Blocks for Target/Action?

Blocks are an awesome extension to C. And, since iOS 4 (the first version that supported blocks) now has enough adoption to make it safe to be minimum version that I support, I’ve been checking out blocks quite a bit to see where they could make blocks easier.

Some of the UIKit and Foundation APIs have been updated to use blocks, but many of them are still using the older target/action mechanism or delegation where blocks would probably be a better choice. Lately, there’s been a bunch of projects designed to add block support to the places that Apple hasn’t (or has decided against) adding it.

That got me thinking: blocks are Objective-C objects, why can’t they act as the target, with the body of the block as the action? (Note: I’m not posting this up here as a suggestion that you use this (yet!), but as a way to ask if this makes any sense.)

Since I couldn’t find any documentation on what Objective-C class blocks are (except that they inherit from NSObject), I decided to add a category to NSObject to give me a selector to invoke the block with. To do that, I came up with -[NSObject startBlock], which ended up looking like this: (((void (^)(void)) self)());.

However, when I tried target:^{ NSLog("in a block!"); } action:@selector(startBlock), I got a crash, because the block was never retained (as targets shouldn’t be), so it no longer existed by the time -startBlock was called on it. Just copying it wouldn’t work, either, because it wouldn’t ever be released, and adding an ivar to release it yourself removed any of the advantages of using a block over an additional selector in the first place.

My next idea is when things got a little crazy. Associated objects (added in iOS 3.1) could get around those issues, since they’re automatically released when the object they are attached to is deallocated. I added another method to my NSObject category, -copyWithOwner:, that took advantage of that to force ownership of an object onto another object. Using that, I ended up with something that you use like this: [object addTarget:[^{ NSLog("block!"); } copyWithOwner:self] action:@selector(startBlock)], with the below code used to implement it:

@implementation NSObject (BlockTargetAction)
- (void)startBlock {
    (((void (^)(void)) self)());
}
- (id)copyWithOwner:(NSObject *)owner {
    // copy ourself to the heap
    self = [[self copy] autorelease];

    // this key is unique until self is deallocated,
    // so it should last as long as the owner does
    void *key = (void *) self;

    // automatically released when the owner object is deallocated
    objc_setAssociatedObject(owner, key, self, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    return objc_getAssociatedObject(owner, key);
}
@end

I’m not sure, though, that I’ve fully thought through all the possible implications of using this hack, especially the memory management aspects. Does this make any sense, or am I wasting my time?