【问题标题】:Is there a way to Make NSInvocation surport variable parmas function line [NSstring stringWithFormat:..]有没有办法让 NSInvocation surport 变量 parmas 函数行 [NSstring stringWithFormat:..]
【发布时间】:2015-04-10 06:22:08
【问题描述】:

Apple 文档说“NSInvocation 不支持使用可变数量的参数或联合参数调用方法。”

我搜索了几个小时,有人说 var_list 有效,但我尝试了,它没有

但我认为可能有一种方法可以对变量参数函数做同样的事情(反射),正如我提到的,[stringWithFormat:],

所以,我找到了方法,请阅读下面的代码。

            @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])

为什么函数“fun”的第二次调用有效,而第一次调用失败了?

有没有更好的方法来做到这一点?

【问题讨论】:

  • 1. “论点1”是什么意思? selector?还是第一个 (arr[0]) 或第二个参数 (arr[1]) 传递给该方法? 2. 这个问题的背景是什么,你想完成什么?
  • @AminNegm-Awad 感谢您的评论。我编辑了代码就知道了,对比两个名为 fun 的调用函数可以发现区别,第二个是获取类型转换 ((FloatObj *)) 。为什么第二个函数调用工作正常,但第一个不是。 arr 是一个存储对象的 C 数组,你没看到初始化吗?我想为目标 C 构建运行时反射,例如 Java 反射。
  • 你没有使用 ARC 是吗?因为如果你是,你会 1) 得到一个错误 NSObject 没有声明 realvalue,如果你把它改成 id,你会 2) 仍然得到一个错误,说多个方法 realvalue 不匹配类型。

标签: ios objective-c reflection objective-c-runtime nsinvocation


【解决方案1】:

这与NSInvocation或直接调用方法实现无关。如果您直接调用该方法,您应该得到相同的未定义行为:

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

甚至是常规函数:

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

在 C(和 Objective-C)中,每个表达式都必须有一个编译时类型。编译器需要知道这个编译时类型才能正确编译代码。

所以我问你,[arr[1] realvalue] 的编译时类型应该是什么?是int吗?是float吗?编译器会根据它的类型做不同的事情。例如,如果是float,C 标准规定传递给可变参数的float 将提升为double。在可变参数中传递int 和double 的调用约定是不同的(实际上,这两种类型的大小不同)。并且+[NSString stringWithFormat:] 期望您传递的东西的编译时类型与您在格式字符串中提供的格式说明符相匹配,否则将出现未定义的行为。

从您的格式字符串来看,您似乎希望[arr[1] realvalue] 参数为float 或double,因为您使用了%f。由于FloatObj 是其realvalue 方法返回float 的类,看来将arr[1] 转换为FloatObj * 是正确的做法。

【讨论】:

  • 所以,这是 C 或 objC 的限制,我不能在运行时调用任何带有 varargs 的函数,因为 int 、float 和 struct 的类型在编译时具有不同的大小。看来我必须改变我的想法,也许我需要一个 python 解析器。非常感谢。
  • @cydiyo:问题不在于可变参数;即使没有可变参数,你也会遇到这个问题。问题是你有不同返回类型的同名方法,编译器不知道调用的是哪一个,因此不知道返回类型是什么。要解决此问题,您需要让它们返回相同的类型,可能全部为 double,或者全部为 id(并且您可以有不同类型的对象)。
  • 对于普通方法,我使用 NSInvocation 代替。例如,我在运行时获取带有方法签名的每个参数的类型,但我无法在运行时获取可变参数的类型,这就是问题所在。我的应用读取了一个由字符串“[NSString stringWithFormat:@"%f%d",1.1,2]" 填充的文本文件,该应用需要生成一个字符串@"1.12"
  • 如果文件被其他字符串填充 --"[[[UIAlertView alloc] initWithTitle:@"title" message:"content" delegate:nil cancelButtonTitle:"cancel " otherButtonTitles:"yes" , nil] show]",应用程序显示带有相应元素的警报视图。看了你的回复,我觉得我用 C 或者 ObjC 都做不完
  • @cydiyo: "for normal method..." 您可以获得方法签名(或者如果它是直接方法调用,您已经知道签名),即方法参数的类型。但是,这不是您遇到的问题。假设我们知道方法参数是float。你仍然会遇到这样的问题,如果特定的 realvalue 方法返回一个 float 并且编译器认为它返回一个 int(反之亦然),编译器充其量只会把那个 float 的位当作这是一个int,这将是一个垃圾号码,或者最坏的情况是崩溃。
猜你喜欢
  • 1970-01-01
  • 2021-12-02
  • 1970-01-01
  • 2023-03-21
  • 1970-01-01
  • 1970-01-01
  • 2019-11-15
  • 1970-01-01
  • 2021-11-16
相关资源
最近更新 更多