4

I have a method that dynamically creates new objects of different classes and want to be able to perform a selector on these classes as they are created. Using performSelector: withObject: would had worked but the methods have four arguments. I tried the following code using NSInvocation but got an error about it being an unrecognized selector.

NSInvocation *call = [NSInvocation invocationWithMethodSignature:[NSClassFromString(className) methodSignatureForSelector:@selector(packWithName:value:writer:forClass:)]];
[call setArgument:&arg1 atIndex:0];
[call setArgument:&arg2 atIndex:1];
[call setArgument:&arg3 atIndex:2];
[call setArgument:&arg4 atIndex:3];
call.target = NSClassFromString(className);
[call invoke];

It also produces the following log statement:

*** NSForwarding: warning: selector (0x8ed78d0) for message '[garbled random characters]'
does not match selector known to Objective C runtime (0x8b0cd30)-- abort

I also tried to create the NSInvocation using alloc/init and setting the @selector like this:

NSInvocation *call = [[NSInvocation alloc] init];
call.selector = @selector(nameofselector);

That however results in call being nil, so I guess that's not allowed.

Am I missing something regarding how NSInvocation works or is there a smarter way to do this?

Rick
  • 3,240
  • 2
  • 29
  • 53
  • see here: http://stackoverflow.com/questions/313400/nsinvocation-for-dummies – Iducool Jul 29 '13 at 12:24
  • 1
    Try to avoid calling any APIs in this fashion. You'll be facing lots of runtime errors that could easily be avoided with compilation time checking. This would be required only if you created classes at runtime that cannot be declared statically. Invoking methods on instances that you know the class of requires only casting. This may result in some redundant code but the compiler will have a better chance telling you that you messed up. – allprog Jul 29 '13 at 12:28
  • The problem is that these are class methods and the class is unknown. If they were instance methods it could easily be solved with a protocol and some casting but I have no idea how to solve that with a class. – Rick Jul 29 '13 at 12:37
  • 1
    The code suggests me that this is some archiver functionality. Can you rewrite the solution to comply with NSCoding? – allprog Jul 29 '13 at 12:55
  • Indeed it is. I decided to rewrite it in a different manner, making separate methods for each of the classes. Since the code is generated from a protocol definition it wasn't that much work. Thanks for the tip though :) – Rick Jul 29 '13 at 12:57

2 Answers2

6

Arguments at index 0 and 1 are not the first two explicit arguments of the method call, but the implicit self and _cmd arguments. Use indices 2, 3, 4 and 5 instead.

  • Thanks, that was just the tip I needed. Although I decided to move away from the NSInvocation solution. – Rick Jul 29 '13 at 12:58
4

The Apple documentation just tell that the first argument (with indice 0) represent the target object (so the "self"). As the documentation explains you the first argument is set using the setTarget: method.

So you need to start indices from 2 onwards for using NSInvocation.(that means your code should be like)

NSInvocation *call = [NSInvocation invocationWithMethodSignature:[NSClassFromString(className) methodSignatureForSelector:@selector(packWithName:value:writer:forClass:)]];
[call setArgument:&arg1 atIndex:2];
[call setArgument:&arg2 atIndex:3];
[call setArgument:&arg3 atIndex:4];
[call setArgument:&arg4 atIndex:5];
call.target = NSClassFromString(className);
[call invoke];
Rick
  • 3,240
  • 2
  • 29
  • 53
Aniket Kote
  • 541
  • 3
  • 9