为什么创建NSInvocation必须指定选择器两次

时间:2014-12-15 09:30:12

标签: ios nsinvocation

以下是我在Apple"定时器编程主题"中看到的示例代码:

NSMethodSignature *methodSignature = [self
methodSignatureForSelector:@selector(invocationMethod:)];
NSInvocation *invocation = [NSInvocation
invocationWithMethodSignature:methodSignature];
[invocation setTarget:self];
[invocation setSelector:@selector(invocationMethod:)];
NSDate *startDate = [NSDate date];
[invocation setArgument:&startDate atIndex:2];

如您所见,我们必须在invocationMethod中指定NSMethodSignature:一次,然后在NSInvocation setSelector中指定第二次。 对我来说,这似乎是多余的,苹果设计有这样的理由吗?

3 个答案:

答案 0 :(得分:3)

选择器只是一个字符串,即方法的名称。它不包含有关参数类型或返回类型的信息。方法签名就是类型;它不包含有关方法名称的信息。他们是完全不相交的。

虽然在您的情况下,您通过使用methodSignatureForSelector:询问目标对象来创建方法签名,但您不应该假设人们总是希望这样做。

您可以直接从类型编码的字符串构造方法签名。您可以从另一个方法的签名中获取它并将其应用于此方法。等等。根据具体的用例,可能不适合直接向对象询问方法签名,因为对象可能尚未实现该方法,并且稍后在被调用时会动态添加它。可以有多种原因可以分别指定类型和方法名称的灵活性。

答案 1 :(得分:2)

选择器被传递给两个非常不同的东西。

首先,我们有:

NSMethodSignature *methodSignature = [self methodSignatureForSelector:@selector(invocationMethod:)];

这将生成一个method signature,它描述了返回值类型以及方法的参数的数量和类型。它不记得为此签名创建的选择器(相同的签名对多个选择器有效; -(void)foo:(NSString *)f;-(void)bar:(NSString *)b;具有相同的签名,即使它们的选择器/名称不同)。

然后您创建实际的invocation。它需要知道返回值类型以及参数的数量和类型......所以你用NSMethodSignature初始化它。但它还不知道它应该调用哪个对象和哪个选择器,你明确需要告诉它。这是选择器第二次通过。

您也可以在具有相同签名的调用中将选择器设置为另一个选择器,它也可以正常工作。

答案 2 :(得分:1)

方法签名允许NSInvocation知道NSInvocation可以使用的选择器的结构。创建NSInvocation后,您无法更改调用使用的选择器的结构,但是您可以更改选择器。因此,一个参数用于计算所用方法的结构,另一个是实际调用的方法。

稍后您可以更改它实际调用的方法,前提是它具有与您首先给出的方法签名相同的结构。你可以用一个conince构造函数编写一个类别:

+ (instancetype)invocationWithTarget:(id)target andSelector:(SEL)selector {
  NSMethodSignature *methodSignature = [target methodSignatureForSelector:selector];
  NSInvocation *invocation = [self invocationWithMethodSignature:methodSignature];
  [invocation setTarget:target];
  [invocation setSelector:selector];
  return invocation;
}