Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why creating NSInvocation has to specify selector twice

Here is the example code I saw from Apple's "Timer Programming Topics":

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];

as you can see, we have to specify the invocationMethod: once in the NSMethodSignature, and then second time in NSInvocation's setSelector. To me, this seems redundant, is there any reason why Apple design like this?

like image 678
Samuel Cai Avatar asked Dec 15 '14 09:12

Samuel Cai


3 Answers

A selector is just a string, the name of the method. It contains no information about the types of parameters or return type. A method signature is just the types; it contains no information about the name of the method. They are completely disjoint.

Although in your case you create the method signature by asking the target object using methodSignatureForSelector:, you should not assume that people always want to do it that way.

You could construct a method signature directly from a string of type encodings. You could get it from the signature of another method and apply it to this method. Etc. It might not be appropriate, depending on the particular use case, to directly ask the object for the method signature, because maybe the object doesn't implement that method yet, and will dynamically add it later when called or something. There could be any of a number of reasons why it is useful to have the flexibility of specifying the types and method name separately.

like image 131
newacct Avatar answered Oct 21 '22 10:10

newacct


The selector is passed to two very different things.

First, we have:

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

This generates a method signature which describes the return value type as well as the number and types of arguments of a method. It does not remember which selector this signature was created for (the same signature is valid for multiple selectors; both -(void)foo:(NSString *)f; and -(void)bar:(NSString *)b; have the same signature even though their selectors/names are different).

Then you create the actual invocation. It needs to know the return value type as well as number and types of the arguments… so you initialize it with a NSMethodSignature. But it does not yet know which object and which selector it is supposed to call, you explicitly need to tell it. That's the second time the selector is passed.

You can also set the selector to a different one on the invocation which has the same signature and it will work as well.

like image 29
DarkDust Avatar answered Oct 21 '22 10:10

DarkDust


The method signature lets the NSInvocation know the structure of the selector that the NSInvocation can use. Once you have create a NSInvocation you can't change the structure of the selector that the invocation uses, however you can change the selector. So one argument is used to work out the structure of the method used, and the other is the method that is actually called.

Later you can change the method that it actually called provided it has the same structure as the method signature you gave it in the first place. You could write a category with a convince constructor:

+ (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;
}
like image 25
James Snook Avatar answered Oct 21 '22 11:10

James Snook