Forward invocation
Place in the chain
When objc_msgSend finds no implementation of a selector in the class or its ancestors, the runtime gives the object several chances, from cheap to expensive:
+resolveInstanceMethod:— add the method right now (class_addMethod).-forwardingTargetForSelector:— return another object; the message goes to it as is. Cheap: arguments are not packed.-methodSignatureForSelector:+-forwardInvocation:— full forwarding. The runtime asks for the signature, packsself,_cmdand the arguments into anNSInvocationand hands it to you. You can invoke it on any object, change arguments, swap the selector, log, defer, or return a value viasetReturnValue:.- If
methodSignatureForSelector:returnednilorforwardInvocation:is not overridden —-doesNotRecognizeSelector:→NSInvalidArgumentException.
forwardInvocation: works as a pair: without methodSignatureForSelector: the runtime does not know how to lay out the arguments and never reaches your method.
#import <Foundation/Foundation.h>
@interface LoggingBox : NSObject
@property (nonatomic, strong) id inner;
@end
@implementation LoggingBox
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
NSMethodSignature *sig = [super methodSignatureForSelector:sel];
return sig ?: [self.inner methodSignatureForSelector:sel];
}
- (void)forwardInvocation:(NSInvocation *)invocation {
if ([self.inner respondsToSelector:invocation.selector]) {
NSLog(@"-> %@", NSStringFromSelector(invocation.selector));
[invocation invokeWithTarget:self.inner];
} else {
[super forwardInvocation:invocation]; // doesNotRecognizeSelector:
}
}
- (BOOL)respondsToSelector:(SEL)sel {
return [super respondsToSelector:sel]
|| [self.inner respondsToSelector:sel];
}
@end
The rest of this answer, with code and interview follow-ups, is in the DevPrep app.
Read the full answer in DevPrep