有没有办法将整个参数列表传递给目标C中的另一个方法?

Ove*_*esh 4 reflection objective-c

我希望能够尽可能通用地将方法中接收的所有参数传递给其他方法。

理想情况下,这可以通过传递字典或某些系统变量(类似于_cmd)来完成。

换句话说,我正在寻找类似于argumentsjavascript中的数组之类的东西,或者正在寻找使我能够访问当前调用的方法的参数列表的任何东西。

wm_*_*die 5

我认为您正在寻找的是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如您所见,您想做的事是可能的,但这很难。(看看苹果的类型编码的文件NSMethodSignaturesignatureWithObjCTypes:。)

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,则可以与其他类型一起使用。