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
.
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 -- SEL
s can be compared directly using the equality operator.