Ove*_*esh 4 reflection objective-c
我希望能够尽可能通用地将方法中接收的所有参数传递给其他方法。
理想情况下,这可以通过传递字典或某些系统变量(类似于_cmd)来完成。
换句话说,我正在寻找类似于argumentsjavascript中的数组之类的东西,或者正在寻找使我能够访问当前调用的方法的参数列表的任何东西。
我认为您正在寻找的是NSObject, 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
Run Code Online (Sandbox Code Playgroud)
困难的部分是您需要创建缓冲区来保存参数值。如果所有参数都是对象或相同类型,则可以使用上面的代码,但是如果使用C类型,则泛型函数要复杂得多。您可以使用NSMethodSignature的,getArgumentTypeAtIndex:但是它返回类型和sizeof的字符串编码,将对您没有帮助。size_t对于malloc / calloc,您需要将类型名称映射到s。
编辑:我添加了一个具体的示例,作为我掩盖的东西,// Get the juicy info in methodSignature如您所见,您想做的事是可能的,但这很难。(看看苹果的类型编码的文件和NSMethodSignature的signatureWithObjCTypes:。)
Edit2:这可能是一个更好的单独答案,但这是完整(经过测试)的清单,介绍了如何利用上面的清单制作使用JavaScript中的arguments数组调用的方法。
首先,制定一个委托协议,当方法被调用时,Forwarder对象将调用该委托协议。
@protocol ForwarderDelegate <NSObject>
- (void)selectorCalled:(SEL)selector withArguments:(NSArray *)args;
@end
Run Code Online (Sandbox Code Playgroud)
然后制作实际的转发器:
@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
Run Code Online (Sandbox Code Playgroud)
现在,您可以实例化带有某些对象的转发器,并将所有调用转发给委托。如果目标和委托都是同一个对象,它将像这样工作:
@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
Run Code Online (Sandbox Code Playgroud)
运行此命令应输出如下内容:
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
Run Code Online (Sandbox Code Playgroud)
同样,此版本仅适用于id类型的参数。但是,如果您使用上述TypeEncodings,则可以与其他类型一起使用。