iosobjective-creflectionobjective-c-runtimensinvocation

Is there a way to Make NSInvocation surport variable parmas function line [NSstring stringWithFormat:..]


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?


Solution

  • 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.