Upward Mobility: Dump Those iOS Delegates

Blocks are the way to go

Because so much of iOS programming involves the delegate pattern (the UITableViewDelegate being a prime example), it’s natural that when programmers are developing their own classes that need to be able to asynchronously call back to a client class, they would use the delegate pattern as well. The problem with delegates is that they are fairly inflexible. For example, let’s consider the following (totally inappropriate) use of a delegate:

@protocol MultiplierDelegate <NSObject>
-(void) handleResult:(float) result;
@end

@interface Multiplier : NSObject
-(void) multiply:(float) v1 and:(float) v2
        delegate:(NSObject<MultiplierDelegate> *) delegate;
@end

#import "Multiplier.h"
@implementation Multiplier
(void) multiply:(float) v1 and:(float) v2
       delegate:(NSObject<MultiplierDelegate> *) delegate {
       [delegate handleResult:v1 * v2];
}
@end

The problem lies when you need to call this method twice from the same class.

#import "Multiplier.h"
@interface MultiplierCallee : NSObject<MultiplierDelegate>
@end

@implementation MultiplierCallee
-(void) example {
    Multiplier *mult = [Multiplier new];
         [mult multiply:3.2 and:23.3 delegate:self];
}

-(void) handleResult:(float)result {
   NSLog(@"Result is %f", result);
}
@end

So far, so good, but what happens if you want to have a second method in the same class call multiply? You can only have a single delegate method, so you have to start jumping through hoops to let the delegate method know where to put the resulting value.

The modern solution to this is to use blocks. People tend to shy away from them, because the syntax is a bit arcane and un-Objective-C, but they are incredibly powerful, and Apple has signaled that they are the future, by starting to use them heavily in areas such as animation.

Here’s what our multiply example looks like, recoded to use blocks:

@interface Multiplier : NSObject
-(void) multiply:(float) v1 and:(float) v2
                    resultBlock:(void(^)(float)) resultBlock;
@end

@implementation Multiplier
-(void) multiply:(float) v1 and:(float) v2
     resultBlock:(void(^)(float)) resultBlock {
  resultBlock(v1 * v2);
}
@end

@implementation MultiplierCallee

-(void) example {
    Multiplier *mult = [Multiplier new];
    [mult multiply:3.2 and:23.3 resultBlock:^(float result) {            NSLog(@"Result is %f", result);
     }];
}
@end

 

Not only have we been able to ditch an entire unneeded delegate protocol, but we can now call our multiplier class as many times as we want from the same client class, and each call can have its own distinct handler for the result. An added benefit is that the block has access to any locally bound variables in the calling method, so that you don’t have to keep temporary values around as properties on the class, just so that the handler can have access to them later.

tags: , ,