1

我有一个奇怪的问题,即使用包含未对齐的双精度或任何 64 位类型的结构设置 NSInvocation 的参数(我在结构的开头用 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


4

1 回答 1

2

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

va_list 不一定只是一个指针,它是一种不透明的类型。您对 uint8_t* 的强制转换几乎可以肯定是无效的。

于 2012-04-05T13:37:37.610 回答