Forward Invocation
Место в цепочке
Когда objc_msgSend не находит реализацию селектора ни в классе, ни у предков, рантайм даёт объекту несколько шансов, от дешёвого к дорогому:
+resolveInstanceMethod:— добавить метод прямо сейчас (class_addMethod).-forwardingTargetForSelector:— вернуть другой объект; сообщение уйдёт ему как есть. Дёшево: аргументы не упаковываются.-methodSignatureForSelector:+-forwardInvocation:— полное перенаправление. Рантайм спрашивает сигнатуру, упаковываетself,_cmdи аргументы вNSInvocationи отдаёт вам. Вы можете вызвать её на любом объекте, изменить аргументы, подменить селектор, залогировать, отложить, вернуть значение черезsetReturnValue:.- Если
methodSignatureForSelector:вернулnilилиforwardInvocation:не переопределён —-doesNotRecognizeSelector:→NSInvalidArgumentException.
forwardInvocation: работает в паре: без methodSignatureForSelector: рантайм не знает, как разложить аргументы, и до вашего метода не дойдёт.
#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
Продолжение ответа с кодом и уточняющими вопросами интервьюера — в приложении DevPrep.
Полный ответ — в DevPrep