使用objc_msgSend调用带有命名参数的Objective C函数

Mar*_*man 23 reflection cocoa objective-c

我想使用objc运行时为Objective-C项目添加脚本支持.现在我面临的问题是,我没有线索,我应该如何调用一个带有几个命名参数的Objective-C方法.

例如,下面的Objective-c调用

[object foo:bar];
Run Code Online (Sandbox Code Playgroud)

可以从C调用:

objc_msgSend(object, sel_getUid("foo:"), bar);
Run Code Online (Sandbox Code Playgroud)

但是我如何为方法调用做类似的事情:

[object foo:var bar:var2 err:errVar];
Run Code Online (Sandbox Code Playgroud)

??

最好的马库斯

Ken*_*Ken 45

接受的答案很接近,但对某些类型无效.例如,如果声明方法将float作为其第二个参数,则这将不起作用.

要正确使用objc_msgSend,必须将其强制转换为适当的类型.例如,如果您的方法声明为

- (void)foo:(id)foo bar:(float)bar err:(NSError **)err
Run Code Online (Sandbox Code Playgroud)

那么你需要做这样的事情:

void (*objc_msgSendTyped)(id self, SEL _cmd, id foo, float bar, NSError**error) = (void*)objc_msgSend;
objc_msgSendTyped(self, @selector(foo:bar:err:), foo, bar, error);
Run Code Online (Sandbox Code Playgroud)

使用objc_msgSend尝试上述情况,并注销收到的参数.您将无法在被调用函数中看到正确的值.这种不寻常的投射情况的出现是因为objc_msgSend并不像普通的C函数那样被调用.它(并且必须)在汇编中实现,并且在摆弄几个寄存器之后跳转到目标C函数.特别地,指任何参数过去的前两个从内objc_msgSend没有一致的方法.

另一种直接调用objc_msgSend的情况是不起作用的是返回NSRect的方法,比如因为在这种情况下不使用objc_msgSend,objc_msgSend_stret是.在返回NSRect的方法的底层C函数中,第一个参数实际上是指向out值NSRect的指针,并且函数本身实际返回void.您必须在调用时匹配此约定,因为它是被调用方法将采用的.此外,使用objc_msgSend_stret的情况在体系结构之间是不同的.还有一个objc_msgSend_fpret,它应该用于在某些体系结构上返回某些浮点类型的方法.

现在,既然你正在尝试做一个脚本桥接的事情,你可能无法明确地转换你遇到的每个案例,你想要一个通用的解决方案.总而言之,这并非完全无足轻重,不幸的是,您的代码必须专门针对您希望定位的每个架构(例如i386,x86_64,ppc).你最好的选择可能是看看PyObjC是如何做到的.你还想看看libffi.了解如何在C中传递参数可能是一个好主意,您可以在Mac OS X ABI指南中阅读.最后,在objc运行时工作的Greg Parker 在objc内部编写了一些非常好的帖子.

  • 在标准C中不允许在函数指针类型和`void*`之间进行转换 (3认同)

ken*_*ytm 21

objc_msgSend(object, sel_getUid("foo:bar:err:"), var, var2, errVar);
Run Code Online (Sandbox Code Playgroud)

如果其中一个变量是a float,则需要使用@Ken的方法,或者通过重新解释转换作弊:

objc_msgSend(..., *(int*)&var, ...)
Run Code Online (Sandbox Code Playgroud)

此外,如果选择器返回一个浮点数,您可能需要使用objc_msgSend_fpret,如果它返回一个必须使用的结构objc_msgSend_stret.如果这是对超类的调用,则需要使用objc_msgSendSuper2.

  • (上面的重新解释也是严格的别名违规.好的,我已经完成了!抱歉哈利!) (4认同)
  • 这是接近的,但是对于采用某些类型的方法不会按预期工作.例如,假设声明该方法为其中一个参数采用浮点数.objc_msgSend被声明为varargs函数,传递给它的任何float都将被提升为double.然后,objc_msgSend的impl将跳转到期望浮点的方法,并且它将不起作用.详情请见我的回答. (3认同)
  • Annnnnd重新解释演员的东西也不会可靠地运作.;-)我认为有点转换为int因为int和float具有相同的大小?不幸的是,即使它们具有相同的大小,所有体系结构也不会以相同的方式传递整数和浮点数.施放到浮动没有好处,因为它只是被提升为双倍.真的,你需要强制转换函数指针. (3认同)
  • 关于编辑:并非所有体系结构上的所有struct返回都使用objc_msgSend_stret.它因架构和结构中的内容而异. (2认同)