-[NSInvocation getReturnValue:] 使用 double 值意外产生 0 [英] -[NSInvocation getReturnValue:] with double value produces 0 unexpectedly

查看:18
本文介绍了-[NSInvocation getReturnValue:] 使用 double 值意外产生 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.

这是演示问题的代码:

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_msgSendobjc_msgSendStret.我猜现在它还需要知道它在什么平台上运行,这就是错误所在.

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(32 位)和 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 将调用视为返回一个包含双精度值的结构,这使它按预期工作.为此,我将方法签名伪造为返回结构的方法的伪造签名.我把它放在 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);
    }
}

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

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