iOS NSInvocation setArgument:atIndex:不适用于ARM构建的struct

时间:2012-04-01 14:25:46

标签: objective-c ios arm nsinvocation arm7

我有一个奇怪的问题,设置一个NSInvocation的参数,其结构包含一个未对齐的double或任何64位类型(我在结构的开头用一个char来抵消它)。问题是在设置参数后会清除一些字节。在ARM7上会出现此问题,但在iOS模拟器中不会出现此问题。

我正在使用LLVM 3.0和Xcode 4.2

这是我的代码和测试结果:

NSInvocation的+ Extension.h

@interface NSInvocation (Extension)

+ (NSInvocation*) invocationWithTarget: (id)aTarget
                              selector: (SEL)aSelector
                       retainArguments: (BOOL)aRetainArguments, ...;

- (void) setArguments: (va_list)aArgList;
- (void) setArguments: (va_list)aArgList atIndex: (NSInteger)aIndex;

@end    // NSInvocation (Extension)

NSInvocation的+ Extension.m

#import <objc/runtime.h>

#import "NSInvocation+Extension.h"


@implementation NSInvocation (Extension)

+ (NSInvocation*) invocationWithTarget: (id)aTarget
                              selector: (SEL)aSelector
                       retainArguments: (BOOL)aRetainArguments, ...
{
    NSMethodSignature* signature = [aTarget methodSignatureForSelector: aSelector];
    NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: signature];

    if (aRetainArguments)
    {
        [invocation retainArguments];
    }
    [invocation setTarget: aTarget];
    [invocation setSelector: aSelector];

    va_list argList;
    va_start(argList, aRetainArguments);
    [invocation setArguments: argList];
    va_end(argList);

    return invocation;
}

- (void) setArguments: (va_list)aArgList
{
    [self setArguments: aArgList atIndex: 0];
}

- (void) setArguments: (va_list)aArgList atIndex: (NSInteger)aIndex
{
    // Arguments are aligned on machine word boundaries
    const NSUInteger KOffset = sizeof(size_t) - 1;

    UInt8* argPtr = (UInt8*)aArgList;
    NSMethodSignature* signature = [self methodSignature];

    // Indices 0 and 1 indicate the hidden arguments self and _cmd respectively.
    for (int index = aIndex + 2; index < [signature numberOfArguments]; ++index)
    {
        const char* type = [signature getArgumentTypeAtIndex: index];
        NSUInteger size = 0;
        NSGetSizeAndAlignment(type, &size, NULL);
        [self setArgument: argPtr atIndex: index];
        argPtr += (size + KOffset) & ~KOffset;
    }
}

@end  // NSInvocation (Extension)

声明要调用的方法和数据结构

- (void) arg1: (char)aArg1 arg2: (char)aArg2 arg3: (TEST)aArg3 arg4: (char)aArg4;

typedef struct test {
    char c;
    double s;
    char t;
    void* b;
    char tu;
} TEST;

致电代码

TEST df = { 'A', 12345678.0, 'B', (void*)2, 'C' }; 

char buf[100] = {0};

NSInvocation* ik = [NSInvocation invocationWithTarget: self selector: @selector(arg1:arg2:arg3:arg4:) retainArguments: NO, '1', '2', df, '3'];
[ik getArgument: &buf atIndex: 4];

ARM7上buf的内容(字节8,9,10和11设置为零,这搞砸了双值)

41 00 00 00 00 00 00 00 29 8C 67 41 42 00 00 00 02 00 00 00 43 00 00 00

i386模拟器上的buf内容(如预期的那样)

41 00 00 00 00 00 00 C0 29 8C 67 41 42 00 00 00 02 00 00 00 43 00 00 00


1 个答案:

答案 0 :(得分:2)

首先想到的是你真的必须使用va_arg来访问可变参数列表中的连续参数。你无法假设参数是像你一样安排在一个很好的连续内存中。首先,ARM ABI说前四个参数是在寄存器中传递的。

va_list不一定只是一个指针,它是一个不透明的类型。你对uint8_t *的演员几乎肯定无效。