0

Apple doc says "NSInvocation does not support invocations of methods with either variable numbers of arguments or union arguments. "

i searched for hours ,some people says var_list works, but i tryed ,it does Not

but I think there may be a way to do the same thing (reflection) on variable params function,as i metioned ,[stringWithFormat:],

so , I found a way ,please readt the code bellow .

            @interface INTObj : NSObject
    @property (nonatomic, assign)   int  realvalue
    @end

    @interface FloatObj : NSObject
    @property (nonatomic, assign)   float  realvalue
    @end

    // here ,the selectorName is only know in runtime
    NSString *selectorName = @"stringWithFormat:";
    SEL selector = NSSelectorFromString(selectorName);
    typedef id (*Obj_Imp)(id,SEL,...);
    Method md =  class_getClassMethod(obj, selector);  // here, obj means NSString
    Obj_Imp fun =  (Obj_Imp )method_getImplementation(md); // stringWithFormat:...

    NSString *arg1 = @"hello  %f  %d";

    FloatObj *fo = [[FloatObj alloc] init];
    fo.realvalue = 11.3;

    INTObj *io = [[INTObj alloc] init];
    io.realvalue = 5;


    NSObject *arr[3] = {arg1, fo,io};

   // it cracks  . exc_bad_Access code = 1
   NSString *result = fun(obj , selector, arr[0], [arr[1] realvalue],[arr[2] realvalue]) ;

   // it works but i cant do this ,because i don't know the type of the 4th parameters  at Runtime
   NSString *result = fun(obj , selector, arr[0],[(FloatObj *)arr[1] realvalue],[arr[2] realvalue])

why does the second calling of the function "fun" works while the first one cracks?

is there a better way to to do this?

cydiyo
  • 3
  • 2
  • 1. What do you mean with "argument 1"? `selector`? Or the first (`arr[0]`) or second argument (`arr[1]`)passed to the method? 2. What is the context of that problem, what do you want to accomplish? – Amin Negm-Awad Apr 10 '15 at 08:42
  • @AminNegm-Awad thank you for your Comment. I edited the code just know, comparing the 2 calling function named fun you can find the difference , the second one is get a type casting ((FloatObj *)) . why the second function calling works fine ,but the first not. the arr is a C array that store objects , dont you see the initalization ? I want to build a runtime reflection for objective C , like Java reflection . – cydiyo Apr 10 '15 at 13:37
  • You're not using ARC are you? Because if you were, you would 1) get an error that `NSObject` does not declare `realvalue`, and if you changed it to `id`, you would 2) still get an error that says multiple methods `realvalue` with mismatched types. – newacct Apr 10 '15 at 20:18

1 Answers1

1

This has nothing to do with NSInvocation or calling the method implementation directly. You should get the same undefined behavior if you called the method directly:

NSString *result = [NSString stringWithFormat:arr[0], [arr[1] realvalue], [arr[2] realvalue]];

or even a regular function:

NSLog(arr[0], [arr[1] realvalue], [arr[2] realvalue]);

In C (and Objective-C) every expression must have a compile-time type. The compiler needs to be know this compile-time type be able to compile the code correctly.

So let me ask you, what should be the compile-time type of [arr[1] realvalue]? Is it int? Is it float? The compiler will do different things depending on what type it is. If it is float for example, the C standard says that float passed to varargs will be promoted to double. The calling conventions for passing an int and a double in varargs are different (in fact, these two types have different sizes). And +[NSString stringWithFormat:] expects the compile-time type of the thing you pass to match the format specifier you give it in your format string, or there will be undefined behavior.

From your format string, it seems like you wanted the [arr[1] realvalue] argument to be float or double since you used %f. Since FloatObj is the class whose realvalue method returns float, it seems that casting arr[1] to FloatObj * is the right thing to do.

newacct
  • 119,665
  • 29
  • 163
  • 224
  • so, it is the limitation of C or objC ,I can't call any function with varargs on runtime ,because the type of int ,float,and struct have different sizes in complie-time. it seems that I have to change my idea,maybe I need a python parser. thanks a lot. – cydiyo Apr 11 '15 at 05:18
  • @cydiyo: The problem isn't with varargs; even without varargs you would have this problem. The problem is that you have methods of the same name with different return types, and the compiler doesn't know which one is called, and thus doesn't know what the return type is. To fix this you need to have them return the same type, perhaps all be `double`, or all be `id` (and you can have different types of objects). – newacct Apr 11 '15 at 05:50
  • for normal method ,I use NSInvocation instead. I get the types of each parameter with method signature on runtime ,but i can't get types of the varargs on runtime ,that is the problem,for example. my app read a text file which is filled by string "[NSString stringWithFormat:@"%f%d",1.1,2]", the app need generate a string @"1.12" – cydiyo Apr 11 '15 at 06:42
  • if the file is filled by an other string --"[[[UIAlertView alloc] initWithTitle:@"title" message:"content" delegate:nil cancelButtonTitle:"cancel " otherButtonTitles:"yes", nil] show]",the app show a alertview with corresponding elements. after reading your reply ,I don't think I can finish the job with C or ObjC – cydiyo Apr 11 '15 at 06:43
  • @cydiyo: "for normal method..." You can get the method signature (or if it's a direct method call you already know the signature), i.e. what types the method parameters are. However, that's not the issue you're having. Suppose we know the method parameter is `float`. You will still have the problem that if the particular `realvalue` method returns a `float` and the compiler thinks it returns an `int` (or vice versa), the compiler will at best take the bits of that `float` as if it were an `int`, which will be a garbage number, or at worst crash. – newacct Apr 11 '15 at 07:57