Ios -[NSInvocation getReturnValue:]具有双精度值时意外生成0

Ios -[NSInvocation getReturnValue:]具有双精度值时意外生成0,ios,objective-c,ipad,nsinvocation,arm64,Ios,Objective C,Ipad,Nsinvocation,Arm64,我试图调用一个方法,该方法使用NSInvocation返回一个double。但我发现它在64位iOS应用程序中不起作用。它可以在OS X上工作,在模拟器中——32位和64位——iPad2和32位构建的iPadAir上工作。只有iPad Air设备上的64位内置设备存在此问题 这是演示问题的代码: NSMethodSignature *signature = [NSString instanceMethodSignatureForSelector:@selector(doubleValue)];

我试图调用一个方法,该方法使用
NSInvocation
返回一个
double
。但我发现它在64位iOS应用程序中不起作用。它可以在OS X上工作,在模拟器中——32位和64位——iPad2和32位构建的iPadAir上工作。只有iPad Air设备上的64位内置设备存在此问题

这是演示问题的代码:

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);
}
64位内置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
值上

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

你现在没有希望让它工作。我尝试了您的许多代码变体,并在iPhone 5S和最新的模拟器上以32位和64位模式进行了测试,但这一定是arm64的一个错误,因为64位模拟器工作正常

因此,首先我修改了您的代码以尝试各种变体,结果使用
floatValue
也失败了。由于浮动的大小是常见的,这减少了各种试验平台之间的变量数量

此外,我还尝试使用使用整数和浮点方法创建的NSNumber目标:浮点方法实际上会导致崩溃!我尝试了其他选项,如保留字符串和设置调用保留设置,不做任何更改

我在此输入了一个错误:
15441447:NSInvocation仅在arm64设备上失败
-任何关心它的人都可以复制它。我还上传了一个演示项目

我上传的代码是:

- (void)test
{
    NSMethodSignature *signature = [NSString instanceMethodSignatureForSelector:@selector(floatValue)];
    for (int i = 0; i < 10; i++) {
        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];

#if 0
        NSString *str = [NSString stringWithFormat:@"%d", i];
        [invocation setTarget:str]; // fails on iPhone 5s
#else
        //[invocation setTarget:[NSNumber numberWithFloat:(float)i]]; // crashes in 'invoke' on iPhone 5s, works fine in simulator
        [invocation setTarget:[NSNumber numberWithInteger:i]]; // fails on iphone 5S
#endif
        [invocation setSelector:@selector(floatValue)];

        [invocation invoke];

        float f;
        [invocation getReturnValue:&f];

        NSLog(@"%f", f);
    }
}
-(无效)测试
{
NSMethodSignature*signature=[NSString instanceMethodSignatureForSelector:@selector(floatValue)];
对于(int i=0;i<10;i++){
NSInvocation*invocation=[NSInvocation invocationWithMethodSignature:signature];
#如果0
NSString*str=[NSString stringWithFormat:@“%d”,i];
[invocation setTarget:str];//在iPhone 5s上失败
#否则
//[invocation setTarget:[NSNumber numberWithFloat:(float)i]];//在iPhone 5s上的“invoke”中崩溃,在模拟器中工作正常
[invocation setTarget:[NSNumber numberWithInteger:i]];//在iphone 5S上失败
#恩迪夫
[调用集合选择器:@selector(floatValue)];
[调用];
浮动f;
[调用getReturnValue:&f];
NSLog(@“%f”,f);
}
}

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

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

我对您的代码进行了一些研究,结果显示,在arm64上,双返回值是按结构传递的,而
NSInvocation
将其视为在寄存器中传递。我不知道哪一方是正确的。(我只知道在这方面会很危险。我祈祷有比我更低级的人来读这篇文章,并给出更好的解释!)

也就是说,在我看来,在arm(32位)和arm64中传递参数和结果的方式发生了重大变化。请参阅和(非64位)中的结果返回部分

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

由于不知道具体是什么被破坏了,或者修复后会发生什么,我不太可能用这个“修复”来发布代码。我会找到其他的解决办法

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);
    }
}
typedef结构
{
双d;
}双重结构;
@接口字符串(TS)
-(doubleStruct)ts_doubleStructValue;
@结束
@实现NSString(TS)
-(doubleStruct)ts_doubleStructValue
{
双结构ds;
返回ds;
}
@结束
-(无效)试验
{
NSMethodSignature*signature=[NSString instanceMethodSignatureForSelector:@selector(ts_doubleStructValue)];
对于(int i=0;i<10;i++){
NSInvocation*invocation=[NSInvocation invocationWithMethodSignature:signature];
NSString*str=[NSString stringWithFormat:@“%d”,i];
[调用setTarget:str];
[调用集合选择器:@selector(doubleValue)];
[调用];
双d;
[调用getReturnValue:&d];
NSLog(@“%lf”,d);
}
}

基于@TomSwift answer进行修复

    - (void)testInvocation
    {
        NSInvocation *invocation = [[self class] invocationWithObject:self selector:@selector(getAFloat)];

        [invocation setTarget:self];
        [invocation invoke];

        double d;
        [invocation getReturnValue: &d];
        NSLog(@"d == %f", d);

        return YES;
    }

    + (NSInvocation *)invocationWithObject:(id)object selector:(SEL)selector
    {
        NSMethodSignature *sig = [object methodSignatureForSelector:selector];
        if (!sig) {
            return nil;
        }

    #ifdef __LP64__
        BOOL isReturnDouble = (strcmp([sig methodReturnType], "d") == 0);
        BOOL isReturnFloat = (strcmp([sig methodReturnType], "f") == 0);

        if (isReturnDouble || isReturnFloat) {
            typedef struct {double d;} doubleStruct;
            typedef struct {float f;} floatStruct;

            NSMutableString *types = [NSMutableString stringWithFormat:@"%s@:", isReturnDouble ? @encode(doubleStruct) : @encode(floatStruct)];
            for (int i = 2; i < sig.numberOfArguments; i++) {
                const char *argType = [sig getArgumentTypeAtIndex:i];
                [types appendFormat:@"%s", argType];
            }

            sig = [NSMethodSignature signatureWithObjCTypes:[types UTF8String]];
        }
    #endif

        NSInvocation *inv = [NSInvocation invocationWithMethodSignature:sig];
        [inv setSelector:selector];
        return inv;
    }
-(无效)测试位置
{
NSInvocation*invocation=[[self-class]invocationWithObject:self-selector:@selector(getAFloat)];
[调用setTarget:self];
[调用];
双d;
[调用getReturnValue:&d];
NSLog(@“d==%f”,d);
返回YES;
}
+(NSInvocation*)调用WithObject:(id)对象选择器:(SEL)选择器
{
NSMethodSignature*sig=[对象方法签名选择器:选择器];
如果(!sig){
返回零;
}
#ifdef_uulp64__
BOOL isReturnDouble=(strcmp([sig methodReturnType],“d”)==0);
BOOL isReturnFloat=(strcmp([sig methodReturnType],“f”)==0;
如果(isReturnDouble | | isReturnFloat){
typedef结构{double d;}double结构;
    - (void)testInvocation
    {
        NSInvocation *invocation = [[self class] invocationWithObject:self selector:@selector(getAFloat)];

        [invocation setTarget:self];
        [invocation invoke];

        double d;
        [invocation getReturnValue: &d];
        NSLog(@"d == %f", d);

        return YES;
    }

    + (NSInvocation *)invocationWithObject:(id)object selector:(SEL)selector
    {
        NSMethodSignature *sig = [object methodSignatureForSelector:selector];
        if (!sig) {
            return nil;
        }

    #ifdef __LP64__
        BOOL isReturnDouble = (strcmp([sig methodReturnType], "d") == 0);
        BOOL isReturnFloat = (strcmp([sig methodReturnType], "f") == 0);

        if (isReturnDouble || isReturnFloat) {
            typedef struct {double d;} doubleStruct;
            typedef struct {float f;} floatStruct;

            NSMutableString *types = [NSMutableString stringWithFormat:@"%s@:", isReturnDouble ? @encode(doubleStruct) : @encode(floatStruct)];
            for (int i = 2; i < sig.numberOfArguments; i++) {
                const char *argType = [sig getArgumentTypeAtIndex:i];
                [types appendFormat:@"%s", argType];
            }

            sig = [NSMethodSignature signatureWithObjCTypes:[types UTF8String]];
        }
    #endif

        NSInvocation *inv = [NSInvocation invocationWithMethodSignature:sig];
        [inv setSelector:selector];
        return inv;
    }