Objective-C 中是否有类似 JavaScript apply 方法的功能?

5

基本上,我正在使用window.location="myobj:mymethod:myarg:myotherarg"实现处理objc中JavaScript调用的典型方式,但是,我想知道是否有一种方法可以将参数数组应用于方法,类似于JavaScript中的方法。

通常,我一直在这样做

-(void) mymethod:(NSArray*) arr{
    //method knows how many arguments it takes and what they mean at each index
}

我更喜欢做:

-(void) mymethod:(NSString*) myarg myOtherArg: (NSString*) myotherarg{
    //do stuff
}

并且需要有一个像这样的方法:
+(void) callMethod:(NSString*)selectorName withArgs: (NSArray*)args onObject:(id) obj{
    //implementation
}
[JEHelpers callMethod:selector withArgs:someArrayOfArgs onObject:myapp]

这是可能的吗?
4个回答

2
如果你知道没有任何方法需要超过两个参数,你可以使用 performSelector:withObject:withObject: 来进行这些调用。如果该方法需要的参数少于两个,未使用的 withObject: 字段将被忽略。
+ (id)callMethod:(NSString *)selectorName withArgs:(NSArray *)args onObject:(id)obj {
    id arg1 = nil, arg2 = nil;
    if([args count]) {
        arg1 = [args objectAtIndex:0];
        if([args count] > 1])
           arg2 = [args objectAtIndex:1];
    }
    return [obj performSelector:NSSelectorFromString(selectorName)
                     withObject:arg1 withObject:arg2];
}

如果有超过两个参数,你将需要使用NSInvocation。这个类允许你通过传递各种参数并定义选择器和对象来构造一条消息,然后发送消息并获取结果。
+ (id)callMethod:(NSString *)selectorName withArgs:(NSArray *)args onObject:(id)obj {
    SEL sel = NSSelectorFromString(selectorName);
    NSMethodSignature *signature = [obj methodSignatureForSelector:sel];
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
    [invocation setSelector:sel];
    [invocation setTarget:obj];
    NSUInteger index = 2;
    for(id arg in args) {
        [invocation setArgument:&arg atIndex:index];
        ++index;
    }
    id result;
    [invocation setReturnValue:&result];
    [invocation invoke];
    return result;
}

是的...但重点在于能够像JS apply函数一样,对一个方法应用一组动态参数,以创建一个非常干净的桥梁,将WebView JS函数调用与本地Objective-C方法联系起来。因此,基本上我可以有另一个名为JSFunctionMethods的Objective-C类,它将反映我在Webview中实现的JS API中的JS调用的API。感谢您的意见,并希望我已经更好地阐明了问题。 - Jesse Earle
@Jesse 我刚才发了一篇帖子,是在回忆如何使用NSInvocation。请看我的补充(或者看看@Andrew的回答)。 - ughoavgfhw

2

我不确定我完全理解这个问题(我不太了解JavaScript)。然而,您可以使用NSInvocation向任何对象发送任意消息。像这样:

+(void) callMethod:(NSString*)selectorName withArgs: (NSArray*)args onObject:(id) obj
{
    SEL selector = NSSelectorFromString(selectorName);
    if (![obj respondsToSelector:selector]) {
        // Object doesn't respond to selector, so do something else/handle the error
    }
    NSMethodSignature *methodSignature = [obj methodSignatureForSelector:selector];
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
    [invocation setTarget:obj];
    [invocation setSelector:selector];
    for (NSUInteger i=0; i<[args count]; i++) {
        id argument = [args objectAtIndex:i];
        [invocation setArgument:&argument atIndex:i+2]; // Arg 0 is self, arg 1 is _cmd
    }
    id result;
    [invocation setReturnValue:&result];
    [invocation invoke];
}

1

你可能需要研究一下NSInvocation。具体来说,你需要使用selectorName的方法签名(NSObject -methodSignatureForSelector:)构建一个NSInvocation,然后设置选择器、目标和参数。请注意:你应该检查参数类型(NSMethodSignature -getArgumentTypeAtIndex:,它是否等于'@'),并且传递的参数从2开始索引(因为0和1是对象的self和方法选择器)。

顺便说一句,我不是说这是一个好主意;只有极少数情况下才需要采用这种方法。


我认为对于我的需求,它非常合适。非常感谢! - Jesse Earle

0

有点笨拙,但是确实可以:

- (id)method:(NSString *)arg1, ... {
  va_list args;
  va_start(args, arg1);
  for (NSString *a = arg1; a!= nil; a= va_arg(args, NSString*)) {
    // do stuff
  }
}

传统上,arg1 是一个字符串,用于定义额外参数的数据类型和数量。
你可以这样调用它:
[self method:@"5", @"4", @"3", @"2", @"1"];

你的数组方法可能对大多数目的更清晰。

啊,是的...这就是我想要避免的...我基本上正在寻找一个镜像API在我的JS和Objc代码中,带有一些桥接翻译的方法。谢谢。 - Jesse Earle
我认为你可能更喜欢使用NSInvocation方法,但同样地,我认为值得展示另一种实现方式。(如果你添加一个包装器,这基本上是相同的,只不过是用C而不是Objective-C实现。) - Stephen Darlington

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接