Objective C运行时参数绑定

时间:2009-11-11 11:25:47

标签: objective-c

我希望(在运行时)将参数绑定到函数,就像在boost :: bind中一样 - 有点像下面这样:

-(void)myFuncWithParameter:(NSString*)param {
   NSLog(param);
}

-(void)init {

   UIButton *helloButton = [UIButton buttonWithType:UIButtonTypeCustom];
   [helloButton addTarget:self action:@selector(myFuncWithParameter:@"hello") forControlEvents:UIControlEventTouchUpInside];

}

所以...我正在动态地(在运行时)将值@“hello”绑定到参数。

显然上面的语法不正确。有谁知道这是否可能以及正确的语法?

干杯, 尼克。

2 个答案:

答案 0 :(得分:3)

简短的回答是否定的,或者至少不是那个等级。

长期的答案是,在技术上可以构建类似于使用NSInvocations(和/或forwardInvocation:)的东西,在methodForSelector中做一些聪明的事情:或者通过动态注册方法实现,但它非常棘手,特别是如果你关心速度的话。

如果我有一些代码来构建像这样的curried方法真的值得,我会做的就是这样(写在这个评论中,未经测试);

//FIXME: In a real implementation you would do some mangling, this code will get confused if you have _s in the curried selector, and thus could be exploitable

//This method makes a unique selector by mangling the arguments
- (SEL) selectorForSelector:(SEL)bindSel withString:(NSString *)bindString {
  NSString *mangle = [NSString *stringWithFormat:@"LGBind_%@_%@"], NSStringFromSelector(bindSel), bindString];
  SEL retval = NSSelectorFromString(mangle);

  //Register the imp. You probably want to check if it is already reg
  if (![self respondsToSelector:retval]) {
    class_addMethod([self class], retval, LGBind_IMP, "v@:")l
  }
}

//Generic dispatcher imp
void LGBind_IMP(id self, SEL _cmd) {
  NSString *selectorName = NSStringFromSelector(_cmd);
  NSArray *array [selectorName componentsSeparatedByString:@"_"];

  //Skip index 0; it is @"LGBind"
  NSString *originalSelectorString = [array objectAtIndex:1];
  NSString *originalArgString = [array objectAtIndex:2];

  //Get our the SEL and the IMP
  SEL originalSEL = NSSelectorFromString(originalSelectorString);
  IMP originalIMP = [self methodForSelector:originalSEL];

  //call the original imp
  originalIMP([self class], originalSEL, originalArgString);
}

显然,根据你的确切需要,你可以用不同的方式做某些事情,例如你可以懒洋洋地使用forwardInvocation中的imps,或者在实例的dict中隐藏有关管理选择器的数据,而不是仅将其管理到选择器名称中。

答案 1 :(得分:1)

一般的答案是目标 - 行动机制只允许目标,发送者和接收发送者的消息;因此,如果您需要访问数据,则必须从目标或发件人处获取数据。

一种选择是创建一个表示参数值,方法和对象的绑定的类。这个类有一个动作来调用对象上的方法,传递值。使用此类的实例作为目标。这是一个简单的例子:

@interface UnaryBinder : NSObject {
    id target;
    SEL selector;
    id parameter;
}
@property id target;
@property SEL selector;
@property (retain) id parameter;
-(id)initWithTarget:(id)anObject selector:(SEL)aSelector param:(id)aParameter;
-(void)action:(id)sender;
@end

@implementation UnaryBinder
...
-(void)action:(id)sender {
    [target performSelector:selector withObject:parameter];
}
@end

如果要支持任意数量的参数,则需要使用NSInvocation(如路易所提到的)而不是performSelector:withObject。当然,控件不会保留其目标,因此您需要一些方法来保持UnaryBinder。此时,您可以跳过特殊类,只是将数据存储在控件中,正如您在使用KVP的评论中提到的那样。或者,将操作分解为控制器类,并使用该实例作为目标。在目标行动方面,UnaryBinder及其同类并没有真正提供任何优势。对于相关主题,谷歌“高阶消息”。