Question

I want to use message-forwarding on my SZNUnmanagedReference class. It has this properties:

@property (nonatomic, strong) NSSet *authors;
@property (nonatomic, strong) SZNReferenceDescriptor *referenceDescriptor;

Basically, when an instance of UnmanagedReference receives the message authorsString, it should forward it to referenceDescriptor, which has a method named - (NSString *)authorsStringWithSet:(NSSet *)authors.

So, i wrote this in SZNUnmanagedReference.m:

- (void)forwardInvocation:(NSInvocation *)anInvocation {

    SEL aSelector = anInvocation.selector;

    if ([NSStringFromSelector(aSelector) isEqualToString:NSStringFromSelector(@selector(authorsString))]) {
        NSMethodSignature *signature = [self.referenceDescriptor methodSignatureForSelector:@selector(authorsStringWithSet:)];
        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
        NSSet *authors = [NSSet setWithSet:self.authors];
        [invocation setSelector:@selector(authorsStringWithSet:)];
        [invocation setArgument:&authors atIndex:2];
        [invocation setTarget:self.referenceDescriptor];

        [invocation invoke];
    } else {
        [self doesNotRecognizeSelector:aSelector];
    }
}

- (BOOL)respondsToSelector:(SEL)aSelector {
    if ([super respondsToSelector:aSelector]) {
        return YES;
    } else if ([NSStringFromSelector(aSelector) isEqualToString:NSStringFromSelector(@selector(authorsString))] && [self.referenceDescriptor respondsToSelector:@selector(authorsStringWithSet:)]) {
        return YES;
    } else {
        return NO;
    }
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    NSMethodSignature *signature = [super methodSignatureForSelector:aSelector];
    if (!signature) {  
        signature = [self.referenceDescriptor methodSignatureForSelector:@selector(authorsStringWithSet:)];
    }
    return signature;
}

It all seems to work, the code in the SZNReferenceDescriptor class gets executed. However, I don't know how to get the authorsString back. If I understood the documentation correctly, I think the referenceDescriptor is supposed to send the result back to the original sender of the message. But it doesn't seem to work. In my test class, [unmanagedReference authorsString] returns nil.

Was it helpful?

Solution

The problem is that you're constructing a new NSInvocation object, whose return value isn't accessible at the point where it's needed (the "top" of the message dispatch "stack"). The runtime only knows about the one that it created for you (the argument to forwardInvocation:; that's the one whose return value it will use. All you have to do, then, is to set its return value:

- (void)forwardInvocation:(NSInvocation *)anInvocation {

    if (anInvocation.selector == @selector(authorsString)) {
        id retVal = [self.referenceDescriptor authorsStringWithSet:self.authors];

        [anInvocation setReturnValue:&retVal];   // ARC may require some memory-qualification casting here; I'm compiling this by brain at the moment
    } else {
        [super forwardInvocation:anInvocation];
    }
}

In fact, there's actually no need to create that new invocation; since all you need is the return value of the method, you can just send the message directly (which you could also do if you just implemented authorsString on SZNUnmanagedReference, rather than using the forwarding mechanism).

Also, note that there's no need to convert selectors to and from strings in order to compare them -- SELs can be compared directly using the equality operator.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top