Skip to content

Callback handlers using blocks

sebastienwindal edited this page Dec 6, 2012 · 5 revisions

This method uses blocks to store callback methods registered with the object you want to be notified from. It is very similar to the Callback handlers using selectors method except blocks, a more recent addition to obj-C, are easier to work with and more flexible.

Similarly with this method you just have to provide for a way to register for actions/callbacks, store registered callbacks in your own object, and loop through when you are ready to notify registered objects.

The key is to provide callback/block signature:

typedef void (^progressHandler)(float percentProgress);
typedef void (^completionHandler)(float result);

and registration methods:

-(void) addCalculationProgressHandler:(progressHandler)handler;
-(void) addCalculationResultHandler:(completionHandler)handler;

again I used two private arrays to store callback objects:

@property (nonatomic, strong) NSMutableArray *progressHandlers; // array of progressHandler objects
@property (nonatomic, strong) NSMutableArray *completionHandlers; // array of completionHandler objects.

Unlike with NSInvocation, the blocks are obj-C objects that can be stored in your array as is.

Invoking the callback is as easy as

progressHandlers[0](0.5);

Consuming is also very simple

in a more modern "inline function" style:

[self.bigCalculator addCalculationProgressHandler:^(float percentProgress) {
        // do something with percentProgress...
    }];

notes

This pattern could almost be interchanged with delegation except it supports multiple objects registering to the observed object. This patterns works very well with any kind of child->parent communication such as View->View controller (the typical action workflow, for buttons, etc...).

Pros

  • the cool factor of using blocks
  • any number of arguments in your blocks
  • In many cases (when you don't have too many things to do in your callback), inline code is actually more readable than calling a selector that is hidden somewhere in your file.
  • very easy to consume
  • You have more control since you implement the notification system
  • multiple callbacks possible

Cons

  • More work on the notifier side, again you have to do the work.
  • no easy way to unregister from an action, again you have to implement it.
  • the usual gotchas of using blocks (local variables not accessible by default, ARC/memory management subtleties, etc...)

Gotchas and best practices

Always copy the block before storing it

Blocks are Objective-C objects that very much behave like every other NSObject with a few differences, one of them being that blocks are initially created on the stack (obj-c objects are otherwise created on the heap). Block_copy() or the copy method must be used to move the Block to the heap if the Block is to outlive the current scope.

See this thread for details.

Switch to main thread if needed!

The callback may not be called in the main thread.

Examples in Cocoa

Numerous.

  • download something using a NSURLConnection.
    [NSURLConnection sendAsynchronousRequest:someHTTPRequest
                                       queue:dispatch_get_main_queue()
                           completionHandler:^(NSURLResponse *, NSData *, NSError *) {
                                // do something with dowloaded content
                           }
     ];
  • UIView animation:
    [UIView animateWithDuration:0.3 animations:^{
        // some thing
    } completion:^(BOOL finished) {
        // some other thing
    }];

Back

Clone this wiki locally