majackson
majackson

Reputation: 2963

forwardInvocation not forwarding invocations

I'm currently attempting to make use of forwardInvocation but having some trouble. I want to forward ClassA invocations of a finite set of methods on to another instance (of ClassB) stored as a property within my ClassA, but I also want to have a fallback method within my ClassA that will be executed if the methods are not implemented in ClassB.

Unfortunately, I'm finding that whatever methods are implemented on ClassB, the ClassA methods are always called!

The way I'm implementing this is shown below. Can anyone offer any suggestions as to where I'm going wrong?

static NSSet *forwardMessages;

@implementation ClassA

@synthesize classBInstance;

- (id) initWithDelegate:(id)delegate {
    self = [super init];
    classBInstance = delegate;
    return self;
}  

+ (void) initialize {
    if (self == [ClassA class]) {
        forwardMessages = [[NSSet alloc] initWithObjects: 
                       @"doSomething",
                       @"doSomethingElse",
                       @"anotherThing",
                       @"yetMoreThings",
                       nil];

- (BOOL) respondsToSelector:(SEL)aSelector {
    return [super respondsToSelector:aSelector] || [self isHandledByDelegate:aSelector];
}

- (BOOL) isHandledByDelegate:(SEL)aSelector {
    return [forwardMessages containsObject:NSStringFromSelector(aSelector)] &&
           [classBInstance respondsToSelector:aSelector];
}

- (void)forwardInvocation:(NSInvocation *)anInvocation
{
    BOOL forwardThis = [self isHandledByDelegate: [anInvocation selector]];

    if (forwardThis) {
        [anInvocation invokeWithTarget:classBInstance];
    } else {
        [super forwardInvocation:anInvocation];
    }
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    Class handlingClass = [self isHandledByDelegate:aSelector]? [classBInstance class]:[self class];
    return [handlingClass instanceMethodSignatureForSelector:aSelector];
}

- (void) doSomething {
    // this should be forwarded to classBInstance but is always called!
}

Upvotes: 0

Views: 561

Answers (1)

gaige
gaige

Reputation: 17481

-forwardInvocation is only called by method dispatch if the a method with that signature does not exist on the class being called. If you are just trying to call methods on the delegate if the delegate has those methods, but you want to use default implementations in the class otherwise, you don't need to use -forwardInvocation, just use an override delegate pattern.

- (void)doSomething {
    if ([delegate respondsToSelector: @selector(doSomething)]) {
        [delegate doSomething];
        return;
    }
    // ... default behavior
}

If you really want to use a complex form of this which has a list of methods to override, etc, you're probably going to end up using a subclass of NSProxy to front-end it and make the determination which class to call, but that seems overly complex unless you don't have access to the class you're trying to shim.

Upvotes: 1

Related Questions