Я думаю, что вы ищете NSObject's forwardInvocation:
. Ему передается NSInvocation
объект, который содержит информацию, которую вы хотите. NSInvocation
также имеет хороший метод с именем invokeWithTarget:
, который в значительной степени перенаправляет вызов метода точно так же, как если бы вы вызывали его напрямую.
Среда выполнения вызовет fowardInvocation:
, если вашему объекту будет отправлено сообщение, для которого у него нет метода, при условии, что вы также переопределите methodSignatureForSelector:
, чтобы среда выполнения могла создать объект NSInvocation.
Если все ваши аргументы являются объектами, метод forwardInvocation
будет выглядеть примерно так:
@implementation Forwarder
@synthesize friendObject;
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
return [self.friendObject methodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
NSLog("Forwarding method: %@", [anInvocation selector]);
NSMethodSignature *sig = [anInvocation methodSignature];
// Get the juicy argument list info from [anInvocation methodSignature]
// NOTE: Arguments 0 and 1 are for self and _cmd So we'll skip those.
int numberOfArgs = [[anInvocation methodSignature] numberOfArguments];
// Assuming all arguments are objects.
id objPointer;
NSMutableArray *argArray = [NSMutableArray array];
for (int i = 2; i < numberOfArgs; i++) {
[anInvocation getArgument:&objPointer atIndex:i];
[argArray addObject:objPointer];
}
// Now argArray contains the array of all the arguments.
}
@end
Сложная часть заключается в том, что вам нужно создать буферы для хранения значений аргументов. Если все аргументы являются объектами или того же типа, вы можете использовать приведенный выше код, но гораздо сложнее создать обобщенную функцию, если вы используете типы C. Вы можете использовать NSMethodSignature
getArgumentTypeAtIndex:
, но он возвращает строковую кодировку типа и размера, которая вам здесь не поможет. Вам нужно составить карту имен типов для size_t
s для malloc / calloc.
Редактировать: я добавил конкретный пример того, что я обозначил как // Get the juicy info in methodSignature
Как вы можете видеть, то, что вы хотите сделать, возможно, но это довольно сложно .
(Ознакомьтесь с документацией Apple по кодировкам типов и NSMethodSignature
signatureWithObjCTypes:
.)
Edit2: это может быть лучше в качестве отдельного ответа, но вот полный (и проверенный) список того, как вы можете использовать приведенный выше список для создания метода, который вызывается с массивом arguments, как в JavaScript.
Сначала создайте протокол делегата, который будет вызывать объект Forwarder при вызове метода.
@protocol ForwarderDelegate <NSObject>
- (void)selectorCalled:(SEL)selector withArguments:(NSArray *)args;
@end
Затем создайте фактического экспедитора:
@interface Forwarder : NSObject {
@private
NSObject *interfaceObject;
id<ForwarderDelegate> delegate;
}
// Some object whose methods we want to respond to.
@property (nonatomic, retain) NSObject *interfaceObject;
@property (nonatomic, retain) id<ForwarderDelegate> delegate;
@end
@implementation Forwarder
@synthesize interfaceObject;
@synthesize delegate;
- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector {
return [interfaceObject methodSignatureForSelector:selector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
int numberOfArgs = [[anInvocation methodSignature] numberOfArguments];
NSMutableArray *args = [NSMutableArray array];
id ref;
for (int i = 2; i < numberOfArgs; i++) {
[anInvocation getArgument:&ref atIndex:i];
[args addObject:ref];
}
// Call the method on the interface (original) object.
if ([self.interfaceObject respondsToSelector:[anInvocation selector]]) {
[anInvocation invokeWithTarget:self.interfaceObject];
}
[self.delegate selectorCalled:[anInvocation selector] withArguments:args];
}
@end
Теперь вы можете создать экземпляр сервера пересылки, который получает какой-либо объект и перенаправляет любые вызовы делегату. Если и цель, и делегат являются одним и тем же объектом, он будет работать следующим образом:
@interface testreflectAppDelegate : NSObject <UIApplicationDelegate, ForwarderDelegate> {
UIWindow *window;
}
@end
@implementation testreflectAppDelegate
@synthesize window;
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[window makeKeyAndVisible];
Forwarder *forwarder = [[[Forwarder alloc] init] autorelease];
forwarder.delegate = self;
forwarder.interfaceObject = self;
[((id)forwarder) doFoo:[NSNumber numberWithInt:1]
withBar:[NSNumber numberWithInt:2]];
return YES;
}
- (void)doFoo:(NSNumber *)foo withBar:(NSNumber *)bar {
NSLog(@"doFoo:withBar: called. Args: %d %d", [foo intValue], [bar intValue]);
}
- (void)doFoo:(NSNumber *)foo {
NSLog(@"doFoo called. Args: %d", [foo intValue]);
}
- (void)selectorCalled:(SEL)selector withArguments:(NSArray *)args {
NSLog(@"selectorCalled: %s with %d arguments", selector, [args count]);
[self doFoo:[args objectAtIndex:0]];
}
@end
Запуск этого должен вывести что-то вроде:
testreflect[3098:207] doFoo:withBar: called. Args: 1 2
testreflect[3098:207] selectorCalled: doFoo:withBar: with 2 arguments
testreflect[3098:207] doFoo called. Args: 1
Опять же, эта версия будет работать только с аргументами типа id. Но может работать с другими типами, если вы используете вышеупомянутые TypeEncodings.