【问题标题】:Running performSelector: on an object that returns a double, not id运行 performSelector: 在返回双精度而不是 id 的对象上
【发布时间】:2011-11-11 00:10:16
【问题描述】:

如何在对象上运行任意选择器,其返回是双精度值?例如,我有 obj A,它有方法 -(double)b​​lah;

我该怎么办double res = [obj performSelector:@selector(blah)];? performSelector 返回一个id 类型的对象,所以我应该从 id 转换为 NSInteger 到 double - 这会失去精度吗?

另外,我想使用 obj 的 methodSignatureForSelector(意思是,没有 NSMethodSignature 也没有 NSInvocation),因为它在运行时会消耗大量 CPU。

【问题讨论】:

  • 为什么是NSInteger?为什么不NSNumber
  • Xcode 编译器阻止转换为 NSInteger 以外的任何内容。此外,NSNumber 是一个对象,但该方法返回一个双精度,而不是一个对象。
  • 但是你可以用任何原语初始化 NSNumber。包括双人间。
  • 好吧,我只是不想正确思考,我现在看到了你的问题。

标签: objective-c cocoa performselector


【解决方案1】:

您可能想看看 Objective-C 运行时函数,尤其是 objc_msgSend_fpret

double objc_msgSend_fpret( id self, SEL op, ... )

它将带有浮点返回值的消息发送到类的实例。

performSelector 方法使用objc_msgSend,它返回一个id 类型。

例如:

double res = objc_msgSend_fpret( obj, @selector( blah ) );

您需要导入此 objc 运行时标头:

#import <objc/message.h>

编辑

顺便说一下,这里是 ObjC 运行时参考的链接: http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ObjCRuntimeRef/Reference/reference.html

编辑 2 - 重要

objc_msgSend_fpret 以不同的方式实现,具体取决于 CPU 架构(基本上是 i386 或 x86_64)。

正如我在评论中所说,这些功能是使用汇编实现的,因此它们的实现取决于 CPU 架构。

在x86_64架构下,这个函数返回一个long double

这就是当您将其分配给 double 时它会失败(并返回 NAN)的原因。

还要注意有一个 objc_msgSend_fp2ret 函数。

所以,基本上,我之前的例子是行不通的:

double x = objc_msgSend_fpret( obj, @selector( blah ) );
printf( "Val: %f\n", x );

如您所见,它将打印“NAN”。

要让它工作,你必须这样做:

long double x = objc_msgSend_fpret( obj, @selector( blah ) );
printf( "Val: %Lf\n", x );

这是一个工作示例:

http://www.eosgarden.com/uploads/misc/fp.m

编译使用:

gcc -Wall -framework Foundation -o fp fp.m

【讨论】:

  • objc_msgSend 和朋友在objc/message.h 中声明。在 Mac OS X 上,您可以只导入 objc/objc-runtime.h,但在 iOS 上,您必须导入 objc/message.h。我编辑了答案。
  • @Macmade Weird 的事情,但虽然它应该工作,但它返回 NAN,其中 IMP 方法返回有效结果。不确定我是否遗漏了什么。
  • @Macmade Chuck 会赢,但我不喜欢它创建一个 NSNumber 对象,只是为了转换为 doubleValue。另外,msgSend 仍然不起作用...` long double x = objc_msgSend_fpret( [NSDate date], @selector( timeIntervalSinceReferenceDate ) );` x = NAN.
  • 没有完美的方法。这一切都取决于你想要什么。通常,在处理此类问题时,性能可能会导致不同的方法(这也可能导致问题)。如果您实际编写了 blah 方法,或者它来自库/框架,它也会有所不同。希望你能找到正确的方法:)
【解决方案2】:

如果它是一个没有参数的方法,您可以在从该方法返回的值上使用valueForKey:doubleValue。否则,我认为您将不得不使用 objc_msgSend_fpret 才能使其正常工作。

【讨论】:

  • 从该方法返回的值双精度值,因此在其上使用 'doubleValue' 或 'valueForKey:' 没有意义。
  • @David: valueForKey: 盒子原语,所以是的,我说的确实有道理。
  • 我的立场是正确的。我喜欢你的解决方案。这将是最好的,除了它创建一个临时 NSNumber 对象。
【解决方案3】:

使用最少的内存(valueForKey:使用两个临时对象)并且 msgSend 方法不起作用。

 IMP myImp1 = [obj methodForSelector:@selector(getDouble)];
 double aDouble1 = ((double (*) (id,SEL))myImp1)(obj,@selector(getDouble));

【讨论】:

  • 也许你会解释为什么你认为这是一个更好的解决方案?
  • 没问题 :) 在这种特定情况下,我认为使用 objc_msgSend_fpret 更好,实际上更具可读性。您应该始终谨慎使用函数的强制转换。
  • 绝对更具可读性。安全方面,不确定内部会发生什么,但我无法想象它除了扩展到 IMP 解决方案之外还有更多作用。
  • 其实所有的 objc_msgSend* 函数都是用汇编实现的,所以它可能会做很多不同的事情。
  • @Macmade 我尝试了您的方法以提高可读性,但它不起作用。它只返回 NAN,而 IMP 方法返回有效结果。以 `NSTimeInterval t = objc_msgSend_fpret([NSDate date], @selector(timeIntervalSinceReferenceDate));` 返回 NAN 为例。
猜你喜欢
  • 2014-09-15
  • 1970-01-01
  • 1970-01-01
  • 2019-12-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-14
相关资源
最近更新 更多