【问题标题】:using objc_msgSend to call a Objective C function with named arguments使用 objc_msgSend 调用具有命名参数的 Objective C 函数
【发布时间】:2011-02-04 03:40:13
【问题描述】:

我想为使用 objc 运行时的 Objective-C 项目添加脚本支持。现在我面临一个问题,我不知道应该如何调用一个需要多个命名参数的 Objective-C 方法。

例如下面的objective-c调用

[object foo:bar];

可以从 C 调用:

objc_msgSend(object, sel_getUid("foo:"), bar);

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

[object foo:var bar:var2 err:errVar];

??

最佳马库斯

【问题讨论】:

  • Objecrtive-C 没有命名参数。它具有与方法名称交错的参数。 (命名参数意味着删除名称是一种选择,但事实并非如此)。
  • 鉴于 markus 只是想为脚本访问创建一个通用的桥,而不是一个超高性能的 c 级桥,他不能像这里一样使用使用 NSInvocation:cocoawithlove.com/2008/03/…

标签: objective-c cocoa reflection


【解决方案1】:

接受的答案很接近,但它不适用于某些类型。例如,如果该方法被声明为将浮点数作为其第二个参数,这将不起作用。

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

- (void)foo:(id)foo bar:(float)bar err:(NSError **)err

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

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);

仅使用 objc_msgSend 尝试上述情况,并注销收到的参数。您不会在被调用函数中看到正确的值。之所以会出现这种不寻常的强制转换情况,是因为 objc_msgSend 不打算像普通的 C 函数那样被调用。它是(并且必须)在汇编中实现的,并且在摆弄几个寄存器后只是跳转到目标 C 函数。特别是,没有一致的方式来引用 objc_msgSend 中前两个之后的任何参数。

直接调用 objc_msgSend 不起作用的另一种情况是返回 NSRect 的方法,例如,因为在这种情况下不使用 objc_msgSend,而使用 objc_msgSend_stret。在返回 NSRect 的方法的底层 C 函数中,第一个参数实际上是指向输出值 NSRect 的指针,而函数本身实际上返回 void。您在调用时必须匹配此约定,因为它是被调用方法所假定的。此外,使用 objc_msgSend_stret 的环境因架构而异。还有一个 objc_msgSend_fpret,它应该用于在某些架构上返回某些浮点类型的方法。

现在,由于您正在尝试做一个脚本桥接的事情,您可能无法明确地转换您遇到的每个案例,您需要一个通用的解决方案。总而言之,这并非微不足道,不幸的是,您的代码必须专门针对您希望定位的每个架构(例如 i386、x86_64、ppc)。您最好的选择可能是看看PyObjC 是如何做到的。您还想看看libffi。了解更多关于如何在 C 中传递参数可能是一个好主意,您可以在 Mac OS X ABI Guide 中阅读。最后,在 objc 运行时工作的 Greg Parker 在 objc 内部写了 a bunch of very nice posts

【讨论】:

  • 在标准 C 中不允许函数指针类型和 void * 之间的转换
【解决方案2】:
objc_msgSend(object, sel_getUid("foo:bar:err:"), var, var2, errVar);

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

objc_msgSend(..., *(int*)&var, ...)

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

【讨论】:

  • 这很接近,但对于采用某些类型的方法将无法按预期工作。例如,假设该方法被声明为参数之一采用浮点数。 objc_msgSend 被声明为一个可变参数函数,并且任何传入它的浮点数都将被提升为双精度。然后 objc_msgSend 的 impl 将跳转到期望浮点数的方法,它不会工作。详情见我的回答。
  • Ken 完全正确;可变参数处理 ABI 和浮点/双精度处理 ABI 可能非常反常,具体取决于 CPU。
  • 关于编辑:并非所有架构上的所有结构返回都使用 objc_msgSend_stret。它因架构和结构中的内容而异。
  • Annnnnd 重新解释演员阵容也不会可靠地工作。 ;-) 我认为转换为 int 的关键是因为 int 和 float 具有相同的大小?不幸的是,整数和浮点数在所有架构上的传递方式并不相同,即使它们具有相同的大小。转换为 float 并没有什么好处,因为它只是被提升为 double。真的,你需要转换函数指针。
  • (上面的重新解释也违反了严格的别名冲突。好的,我完成了!对不起!)
【解决方案3】:

objc_msgSend(obj, @selector(foo:bar:err:), var, var2, &errVar);

【讨论】:

  • 由于 Ken 描述的原因无法正常工作...您需要创建一个特定类型的函数指针。
猜你喜欢
  • 1970-01-01
  • 2018-11-10
  • 2019-03-08
  • 1970-01-01
  • 2015-07-29
  • 2018-11-25
  • 1970-01-01
  • 2011-01-27
  • 1970-01-01
相关资源
最近更新 更多