0

I have an object which defines a set of protocols and delegate methods

Another object(s) respond to them in the usual pattern delegate pattern

   @interface object
   @property (nonatomic,weak) id <myProtocol> delegate;
   @end

object.delegate = someObject;

@interface someObject <object delegate>
// some object delegate methods and their implementation
@end

In many cases - I would like to change the behavior of a particular delegate method (the implementation of the protocol)

In block based syntax I could easily assign a new "block" based on my considerations to the delegating object.

But in the standard delegate pattern this isn't possible. One way to solve the problem would be to create a large dispatch table ("if" or "switch" statement inside of the answering delegate) - but this would be awkward and make it very hard to understand the code.

it would be much easier to write something like

//standard case
theObject.delegate.blockForMethodOne = ^{the usual code to run} // perhaps how to update a UITableView

if (some condition happened) //something was selected something for instance
{
   theObject.delegate.blockForMethodOne = ^ { some code to run in that case }
}

With out this type of syntax we would need to write something similar

-(void)methodOne
{
  if (standard case)
  {
    //standard code
  }
  else if (self.someConditionHappended) // awkward variable in the object to track changes
  {
     // the code in this case
  }
  // and so on
}

I've seen answers but they aren't good enough.

Something that could dynamically generate a selector and block would be much better (a proxy delegate)

Any idea how to create this?

Edit:

Based on some blogs I created this sample class which will answer any delegate methods and forward them

@interface DelegateManager : NSObject

@property (nonatomic,weak) id proxiedObject;
@property (nonatomic) BOOL justResponded;
@property (nonatomic) BOOL logOnNoResponse;

-(id)init;
-(void)forwardInvocation:(NSInvocation*)invocation;
-(id)proxiedObject;
-(void)setProxiedObject:(id)proxied;
-(BOOL)justResponded;
-(void)setLogOnNoResponse:(BOOL)log;
-(BOOL)logOnNoResponse;



@end

@interface NSMethodSignature (objctypes)
+(NSMethodSignature*)signatureWithObjCTypes:(const char*)types;
@end

@implementation DelegateManager
-(id)init
{
    self = [super init];
    if (self)
    {
        self.proxiedObject   = nil;
        self.justResponded   = NO;
        self.logOnNoResponse = NO;
    }

    return self;
}

-(NSMethodSignature*)methodSignatureForSelector:(SEL)selector
{
    NSMethodSignature *sig;
    sig=[[self.proxiedObject class] instanceMethodSignatureForSelector:selector];
    if(sig==nil)
    {
        // sig=[NSMethodSignature signatureWithObjCTypes:"@^v^c"];
        sig = [[NSObject class] instanceMethodSignatureForSelector: @selector(init)];
    }
    self.justResponded=NO;
    return sig;
}

-(void)forwardInvocation:(NSInvocation*)invocation
{
    if(self.proxiedObject==nil)
    {
        if(self.logOnNoResponse)
            NSLog(@"Warning: proxiedObject is nil! This is a debugging message!");
        return;
    }
    if([self.proxiedObject respondsToSelector:[invocation selector]])
    {
        [invocation invokeWithTarget:self.proxiedObject];
        self.justResponded=YES;
    }
    else if(self.logOnNoResponse)
    {
        NSLog(@"Object \"%@\" failed to respond to delegate message \"%@\"! This is a debugging message.",[[self proxiedObject] class],NSStringFromSelector([invocation selector]));
    }
    return;
}


@end

This code works as follows

myobject.delegate = self.delegateManager; // always sets itself to this internal "proxy" for the delegate

-(void)setDelegate(id<myProtocol>)delegate
{
  [self.delegateManager setProxiedObject:delegate];
}

messages are always sent to the proxy which will try to pass them forward

myObject:

[self.delegateManager callADelegateMethod:self];

delegateManager is responsible for passing the message forward

it seems that this could be expanded upon by doing something like

if (something happened in my object)
{
// replace the implementation of the selector my delegate supplies
  [self.myObject.delegateManager setBlock:someBlock forSelector:the selector of the delegate];
}

in delegateManager

-(void)setBlock:(someBlock)block forSelector:(selector)aSelector
{
   [self.dictionary setObject:block forKey:aSelector];
}

-(void)forwardInvocation:(NSInvocation*)invocation
{
  //check if there is an entrance of a block for the particular invocation
}

The question is how to create a key that will be good for this look up?

Community
  • 1
  • 1
Avba
  • 14,822
  • 20
  • 92
  • 192

2 Answers2

0

If you want make thing in the way like that:

theObject.delegate.blockForMethodOne = ^{the usual code to run}
if (some condition happened)
{
   theObject.delegate.blockForMethodOne = ^ { some code to run in that case }
}

There're few questions: are you going to set delegate block (blockForMethodOne) from outside of delegate? so from where would it be called (from inside of delegate?)? on what condition would it be called? If I understand correctly You want to set different behavior for the same block according to some conditions, but what for? you can just call different methods on different conditions.

But if You know what you are doing)) - you should create class (for example Delegate) that will store some block, like that:

typedef void (^BlockCallback)(void);

@interface Delegate : NSObject
@property (nonatomic, copy) BlockCallback blockForMethodOne;
@end

it will store in blockForMethodOne whatever you set there, but it's not delegation pattern anymore. Setting block to somewhere (theObject.delegate.blockForMethodOne = ^{the usual code to run}) would not run it, if you not redefine setter for 'blockForMethodOne', it works just like regular property (just store it), so you have add some logic to call it somehow, at this point I didn't see any benefits of you approach with blocks. Also You can place @property (nonatomic, copy) BlockCallback blockForMethodOne; in protocol declaration, so any class that adopt this protocol should have 'blockForMethodOne' if You want)

Hope helps

in.disee
  • 1,102
  • 1
  • 7
  • 15
0

I think the appropriate solution would be to get rid of the delegate member and the former protocol, and switch to a pure block based API.

Say, your delegate protocol was:

@protocol FooDelegateProtocol <NSObject>
- (void) foo:(Foo*)foo didReceiveData:(NSData*)data;
@end

you may redefine a protocol as follows

typedef void (^foo_receivedDataBlock)(NSData*);

@protocol FooProtocol <NSObject>
@property (nonatomic, copy) foo_receivedDataBlock receivedDataHandler;
@end

Notice, that this is NOT a protocol for the delegate - but it is a protocol for class Foo itself which needs to implement it.

Now, instead of a delegate you have a bunch of properties which are blocks. Instead of setting the delegate of an object of class Foo, you now setup each of the blocks. For each delegate method, there is now a corresponding block.

Now, the call-site is responsible for setting up the blocks.

CouchDeveloper
  • 18,174
  • 3
  • 45
  • 67