1

アラインされていない double または任意の 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 がゼロに設定され、double 値が台無しになります)

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 を使用する必要があるということです。あなたのように、引数が適切な連続したメモリに配置されていると仮定する方法はありません。1 つには、ARM ABI は、最初の 4 つの引数がレジスタで渡されると述べています。

va_list は必ずしも単なるポインターである必要はありません。これは不透明な型です。uint8_t* へのキャストはほぼ確実に無効です。

于 2012-04-05T13:37:37.610 に答える