Note that there are some explanatory texts on larger screens.

plurals
  1. POBest Technique for Replacing Delegate Methods with Blocks
    text
    copied!<p>I'm looking to create a category to replace delegate methods with callbacks blocks for a lot of the simple iOS APIs. Similar to the sendAsyc block on NSURLConnection. There are 2 techniques that are leak free and seem to work fine. What are the pros/cons about each? Is there a better way?</p> <p>Option 1. Use a category to implement the delegate's callback method on NSObject with the external callback block scoped.</p> <pre><code>// Add category on NSObject to respond to the delegate @interface NSObject(BlocksDelegate) - (void) alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex; @end @implementation NSObject(BlocksDelegate) - (void) alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex { // Self is scoped to the block that was copied void(^callback)(NSInteger) = (id)self; // Call the callback passed if callback(buttonIndex); [self release]; } @end // Alert View Category @implementation UIAlertView (BlocksDelegate) + (id) alertWithTitle:(NSString*)title message:(NSString*)message clickedBlock:(void(^)(NSInteger))buttonIndexClickedBlock cancelButtonTitle:(NSString*)cancelButtonTitle otherButtonTitles:(NSString*)otherButtonTitles { // Copy block passed in to the Heap and will stay alive with the UIAlertView UIAlertView *alert = [[UIAlertView alloc] initWithTitle:title message:message delegate:[buttonIndexClickedBlock copy] cancelButtonTitle:cancelButtonTitle otherButtonTitles:otherButtonTitles, nil]; // Display the alert [alert show]; // Autorelease the alert return [alert autorelease]; } @end </code></pre> <p>This adds a lot of methods on the NSObject and seems like it could cause issues with any other class trying to use the standard delegate method. But it keeps the block alive with the object and returns the callback without any leaks that I've found.</p> <hr> <p>Option 2. Create an light-weight class to contain the block, dynamicly associate it with the class so it will stay in the heap and remove it when the callback is complete.</p> <pre><code>// Generic Block Delegate @interface __DelegateBlock:NSObject typedef void (^HeapBlock)(NSInteger); @property (nonatomic, copy) HeapBlock callbackBlock; @end @implementation __DelegateBlock @synthesize callbackBlock; - (id) initWithBlock:(void(^)(NSInteger))callback { // Init and copy Callback Block to the heap (@see accessor) if (self = [super init]) [self setCallbackBlock:callback]; return [self autorelease]; } - (void) dealloc { // Release the block [callbackBlock release], callbackBlock = nil; [super dealloc]; } - (void) alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex { // Return the result to the callback callbackBlock(buttonIndex); // Detach the block delegate, will decrement retain count SEL key = @selector(alertWithTitle:message:clickedBlock:cancelButtonTitle:otherButtonTitles:); objc_setAssociatedObject(alertView, key, nil, OBJC_ASSOCIATION_RETAIN); key = nil; // Release the Alert [alertView release]; } @end @implementation UIAlertView (BlocksDelegate) + (id) alertWithTitle:(NSString*)title message:(NSString*)message clickedBlock:(void(^)(NSInteger))buttonIndexClickedBlock cancelButtonTitle:(NSString*)cancelButtonTitle otherButtonTitles:(NSString*)otherButtonTitles { // Create class to hold delegatee and copy block to heap DelegateBlock *delegatee = [[__DelegateBlock alloc] initWithBlock:buttonIndexClickedBlock]; [[delegatee retain] autorelease]; // Create delegater UIAlertView *alert = [[UIAlertView alloc] initWithTitle:title message:message delegate:delegatee cancelButtonTitle:cancelButtonTitle otherButtonTitles:otherButtonTitles, nil]; // Attach the Delegate Block class to the Alert View, increase the retain count objc_setAssociatedObject(alert, _cmd, delegatee, OBJC_ASSOCIATION_RETAIN); // Display the alert [alert show]; return alert; } @end </code></pre> <p>I like that this doesn't add anything on top of NSObject and things are a little more separated. It's attaching to the instance via the address of the function.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload