2

I have two classes. Delegator uses delegation to send its result. Blocker uses blocks in static methods.

Without changing Delegator, how can I elegantly and easily implement methodWithBlock so that the block is called with the result produced by methodWithDelegate?

Delegator:

@class Delegator;

@protocol Delegate <NSObject>
- (void)delegator:(Delegator *)sender producedResult:(int)result;
@end

@interface Delegator : NSObject
@property (weak, nonatomic) id <Delegate> delegate;
- (void)methodWithDelegate;
@end

@implementation Delegator
- (void)methodWithDelegate
{
    // Some asynchronous computation resulting in
    [self.delegate delegator:self producedResult:42];
}
@end

Blocker:

@interface Blocker : NSObject
+ (void)methodWithBlock:(void(^)(int result))block;
@end

@implementation Blocker
+ (void)methodWithBlock:(void(^)(int result))block
{
    // How to call Delegator's methodWithDelegate and return
    // the result using block ?
    ...
}
@end

Explored solutions:

  • Wrap Delegator into a new class or a category and create a method returning a block, as suggested in this answer. These solutions work but are far too complicated and time-consuming.

  • Make Blocker conform to the protocol Delegate and keep the block in a property, instantiate it within the method methodWithBlock, and call the block when the delegation method is called. This does not work, as there is no strong pointer to this new instance and it gets destroyed.

  • In the previous solution, to avoid losing the instance by lack of a strong pointer, keep a static array of the current instances of Blocker and delete them in the delegate callback method. Again, this solution works but is too complicated.

Community
  • 1
  • 1
  • I'm not sure you can avoid complications in this case. I'd go with number 3, if it's implemented right it should be a winner. Don't be afraid of complicated things :) – Stavash Jan 30 '13 at 16:28
  • It's not really clear what result you're returning from `-methodWithDelegate` since everything in your example code returns `void`. – Jonathan Grynspan Jan 30 '13 at 16:33
  • I'm not entirely sure that I understand the question, but I'm pretty sure I could help you out if I did. Blocker uses static functions that calculate things and then send the result through a block. You call these functions like this: [Blocker methodWithBlock:^(int result) { ... do whatever with result ... }];, what seems to be the problem? What reference are you losing? – Ismael Jan 30 '13 at 16:35
  • @Ismael: I am trying to implement the methodWithBlock method. – Julien Dupuis Feb 01 '13 at 18:58

2 Answers2

0

Rather than delegates and block properties, use an observable property to communicate results. It's cleaner (and easier) as the working object does not have to worry its pretty little head about who may be watching it. And the watching objects don't have to worry about conforming to any special protocol.

Fruity Geek
  • 7,351
  • 1
  • 32
  • 41
  • Yes. Apple seems to be moving in that direction to communicate state in objects like NSOperationQueue and reserving delegation for when you actually want to step in and change/extend functionality without subclassing. – Fruity Geek Jan 30 '13 at 20:02
0

The elegant and easy solution is to just add a block-based method to your Delegator (e.g. through a category). I know you said you can't do that, but we have to admit, then, that any other block based solution where you're creating new classes to yield the effect you want fails the elegance test.

Having said that, if you desperately need block-based methods and cannot extend Delegator, then I'd lean towards option 2, except rather than making those class methods, make them instance methods, and have Blocker instantiate Delegator and define wrapper methods so that classes that would have used Delegator can now use Blocker, but with completion blocks rather than delegate callbacks. And the class that was previously instantiating Delegator can now instantiate Blocker instead.

Rob
  • 415,655
  • 72
  • 787
  • 1,044