- 具有double值的[NSInvocation getReturnValue:]意外地产生0 [英] -[NSInvocation getReturnValue:] with double value produces 0 unexpectedly

查看:144
本文介绍了 - 具有double值的[NSInvocation getReturnValue:]意外地产生0的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用 NSInvocation 调用返回 double 的方法。但我发现它在64位iOS应用程序中不起作用。它适用于OS X,模拟器 - 32位和64位 - iPad 2和带有32位版本的iPad Air。只有iPad Air设备上的64位版本才会出现此问题。

I am trying to call a method that returns a double using NSInvocation. But I found that it does not working in 64 bit iOS apps. It works on on OS X, in the simulator -- both 32-bit and 64 bit -- iPad 2, and iPad Air with a 32-bit build. Only the 64-bit build on an iPad Air device has this problem.

这是演示此问题的代码:

This is the code to demo the problem:

NSMethodSignature *signature = [NSString instanceMethodSignatureForSelector:@selector(doubleValue)];
for (int i = 0; i < 10; i++) {
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
    NSString *str = [NSString stringWithFormat:@"%d", i];
    [invocation setTarget:str];
    [invocation setSelector:@selector(doubleValue)];
    [invocation invoke];
    union {
        double d;
        uint64_t u;
    } d;
    [invocation getReturnValue:&d.d];
    NSLog(@"%lf, %llx", d.d, d.u);
}

预期产出

2013-11-09 22:34:18.645 test[49075:907] 0.000000, 0
2013-11-09 22:34:18.647 test[49075:907] 1.000000, 3ff0000000000000
2013-11-09 22:34:18.648 test[49075:907] 2.000000, 4000000000000000
2013-11-09 22:34:18.649 test[49075:907] 3.000000, 4008000000000000
2013-11-09 22:34:18.650 test[49075:907] 4.000000, 4010000000000000
2013-11-09 22:34:18.651 test[49075:907] 5.000000, 4014000000000000
2013-11-09 22:34:18.652 test[49075:907] 6.000000, 4018000000000000
2013-11-09 22:34:18.653 test[49075:907] 7.000000, 401c000000000000
2013-11-09 22:34:18.654 test[49075:907] 8.000000, 4020000000000000
2013-11-09 22:34:18.654 test[49075:907] 9.000000, 4022000000000000

iPad Air上64位版本的输出

Output for the 64-bit build on iPad Air

2013-11-09 22:33:55.846 test[998:60b] 0.000000, 18710a969
2013-11-09 22:33:55.847 test[998:60b] 0.000000, 18710a969
2013-11-09 22:33:55.848 test[998:60b] 0.000000, 18710a969
2013-11-09 22:33:55.848 test[998:60b] 0.000000, 18710a969
2013-11-09 22:33:55.849 test[998:60b] 0.000000, 18710a969
2013-11-09 22:33:55.849 test[998:60b] 0.000000, 18710a969
2013-11-09 22:33:55.850 test[998:60b] 0.000000, 18710a969
2013-11-09 22:33:55.850 test[998:60b] 0.000000, 18710a969
2013-11-09 22:33:55.851 test[998:60b] 0.000000, 18710a969
2013-11-09 22:33:55.851 test[998:60b] 0.000000, 18710a969

float 值也会发生这种情况。

This also happen for float value.

2013-11-09 23:51:10.021 test[1074:60b] -0.000000, 80000000
2013-11-09 23:51:10.023 test[1074:60b] -0.000000, 80000000
2013-11-09 23:51:10.024 test[1074:60b] -0.000000, 80000000
2013-11-09 23:51:10.024 test[1074:60b] -0.000000, 80000000
2013-11-09 23:51:10.025 test[1074:60b] -0.000000, 80000000
2013-11-09 23:51:10.026 test[1074:60b] -0.000000, 80000000
2013-11-09 23:51:10.026 test[1074:60b] -0.000000, 80000000
2013-11-09 23:51:10.027 test[1074:60b] -0.000000, 80000000
2013-11-09 23:51:10.027 test[1074:60b] -0.000000, 80000000
2013-11-09 23:51:10.028 test[1074:60b] -0.000000, 80000000


推荐答案

同意@David H NSInvocation 在这种情况下被破坏,或者可能是 NSString doubleValue 方法。然而,我能够强迫它工作。

Agreed with @David H that NSInvocation is broken in this case, or possibly the NSString doubleValue method. I was able to force it to work, however.

在我看来, NSInvocation 由于呼叫约定问题/不匹配。通常,objective-c方法的参数和返回值在寄存器中传递。 ( objc_msgSend 知道如何执行这种类型的调用。)但是如果参数或返回值是一个不适合寄存器的结构或类型,那么它们就会被传递在堆栈上。 ( objc_msgSend_stret 执行此类型的调用。) NSInvocation 通常使用方法签名来决定是否需要调用 objc_msgSend objc_msgSendStret 。我猜现在它还需要知道它正在运行什么平台,这就是bug的所在。

It appears to me that NSInvocation is broken due to a calling-convention issue / mismatch. Typically, parameters and return values for objective-c methods are passed in registers. (objc_msgSend knows how to perform this type of call.) But if a parameter or return value is a struct or type that doesn't fit in a register then they're passed on the stack. (objc_msgSend_stret performs this type of call.) NSInvocation typically uses the method signature to be able to decide whether it needs to call objc_msgSend or objc_msgSendStret. I'm guessing that now it also needs to know what platform it's operating on, and this is where the bug lies.

我玩了你的代码了一下它似乎在arm64上,双返回值正在按结构传递,但 NSInvocation 将其视为在寄存器中传递。我不知道哪一方是正确的。 (我知道在这方面只有足够的危险。我的手指越过那个比我来的更低级别的印章并阅读这个,并提供更好的解释!)

I played around with your code a bit and it appears that on arm64 the double return value is being passed as a structure would be, yet NSInvocation is treating it as being passed in a register. I have no idea which side is correct. (I know only enough in this area to be dangerous. My fingers are crossed that someone with more low-level chops than I comes along and reads this, and provides a better explanation!)

那就是说,在我看来,参数和结果在arm(32bit)和arm64中的传递方式有很大的变化。请参阅结果返回部分>针对arm64的ARM过程调用标准 ARM过程调用标准(非64位)。

That said, it appears to me that there are significant changes in how parameters and results are passed in arm (32bit) vs. arm64. See the Result Return sections in both the ARM Procedure Call Standard for arm64 and ARM Procedure Call Standard (non-64 bit).

我能够强制 NSInvocation 到将调用视为返回包含double的结构,这使它按预期工作。为此,我将方法签名伪造成返回结构的方法的伪签名。我把它放在一个 NSString 类别中,但它可以在任何地方生活。

I was able to force NSInvocation to treat the call as returning a struct containing a double, and this made it work as expected. To do this I faked out the method signature to a fake signature of a method returning a struct. I placed this in a NSString category but it could live anywhere.

不知道具体是什么,或者是什么将在修复后发生,我不太可能通过此修复发送代码。我会找到其他一些解决方法。

Not knowing what specifically is broken, or what will happen when it's fixed, I wouldn't likely ship code with this 'fix'. I'd find some other workaround.

typedef struct
{
    double d;

} doubleStruct;

@interface NSString (TS)
- (doubleStruct) ts_doubleStructValue;
@end
@implementation NSString (TS)
- (doubleStruct) ts_doubleStructValue
{
    doubleStruct ds;
    return ds;
}
@end


- (void) test
{
    NSMethodSignature *signature = [NSString instanceMethodSignatureForSelector: @selector( ts_doubleStructValue )];
    for (int i = 0; i < 10; i++) {
        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
        NSString *str = [NSString stringWithFormat:@"%d", i];
        [invocation setTarget:str];
        [invocation setSelector:@selector(doubleValue)];
        [invocation invoke];

        double d;
        [invocation getReturnValue: &d];

        NSLog(@"%lf", d);
    }
}

这篇关于 - 具有double值的[NSInvocation getReturnValue:]意外地产生0的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆