iOS NSInvocation setArgument: atIndex: не работает со структурой в сборках ARM - PullRequest
1 голос
/ 01 апреля 2012

У меня странная проблема с установкой аргумента NSInvocation со структурой, которая содержит двойной или любой 64-битный тип, который не выровнен (я сместил его с символом в начале структуры).Проблема в том, что некоторые байты очищаются после установки аргумента.Эта проблема возникает на 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 (байты 8, 9, 10 и 11 установлены в ноль, что испортило двойное значение)

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

Содержимое буфера на симуляторе i386 (как и ожидалось)

41 00 00 00 00 00 00 C0 298C 67 41 42 00 00 00 02 00 00 00 43 00 00 00


1 Ответ

2 голосов
/ 05 апреля 2012

Первая мысль заключается в том, что вы действительно должны использовать va_arg для доступа к последовательным аргументам в списке переменных аргументов. Невозможно просто предположить, что аргументы расположены в хорошей непрерывной части памяти, как и вы. Во-первых, ARM ABI говорит, что первые четыре аргумента передаются в регистрах.

va_list не обязательно должен быть просто указателем, это непрозрачный тип. Ваше приведение к uint8_t * почти наверняка недействительно.

...