Swizzling метод с переменными аргументами и переслать сообщение - Bad Access

Я реализую «Code Injector Class», который через метод swizzling может дать вам возможность сделать что-то вроде этого:

FLCodeInjector *injector = [FLCodeInjector injectorForClass:[self class]];
[injector injectCodeBeforeSelector:@selector(aSelector:) code:^{
    NSLog(@"This code should be injected");
}];

aSelector может быть методом с переменным числом аргументов и типом возвращаемой переменной. Аргументы / и возвращаемый тип могут быть объектами или примитивным типом.

Сначала я прикрепляю кодinjectCodeBeforeSelector: чтобы вы поняли, что я делаю (я удалил не интересные части кода):

- (void)injectCodeBeforeSelector:(SEL)method code:(void (^)())completionBlock
{

    NSString *selector = NSStringFromSelector(method);

    [self.dictionaryOfBlocks setObject:completionBlock forKey:selector];

    NSString *swizzleSelector = [NSString stringWithFormat:@"SWZ%@", selector];

    // add a new method to the swizzled class
    Method origMethod = class_getInstanceMethod(self.mainClass, NSSelectorFromString(selector));
    const char *encoding = method_getTypeEncoding(origMethod);

    [self addSelector:NSSelectorFromString(swizzleSelector) toClass:self.mainClass methodTypeEncoding:encoding];
    SwizzleMe(self.mainClass, NSSelectorFromString(selector), NSSelectorFromString(swizzleSelector));

}

-(void)addSelector:(SEL)selector toClass:(Class)aClass methodTypeEncoding:(const char *)encoding
{
    class_addMethod(aClass,
                    selector,
                    (IMP)genericFunction, encoding);
}

По сути, я использую class_addMethod, чтобы добавить метод fake / swizzle к классу назначения, а затем выполнить swizzle. Реализация метода устанавливается на функцию, подобную этой:

id genericFunction(id self, SEL cmd, ...) {
    // call the block to inject
    ...
    // now forward the message to the right method, since method are swizzled
    // I need to forward to the "fake" selector SWZxxx

    NSString *actualSelector = NSStringFromSelector(cmd);
    NSString *newSelector = [NSString stringWithFormat:@"SWZ%@", actualSelector];
    SEL finalSelector = NSSelectorFromString(newSelector);

    // forward the argument list
    va_list arguments;
    va_start ( arguments, cmd );

    return objc_msgSend(self, finalSelector, arguments);
}

Теперь проблема: у меня есть EXC_BAD_INSTRUCTION (objc_msgSend_corrupt_cache_error ()) в последней строке. Проблема возникает, если я пересылаю аргументы va_listк поддельному селектору, Если я изменю последнюю строку на

return objc_msgSend(self, cmd, arguments);

без ошибок, но, очевидно, начинается бесконечная рекурсия.

Я пытался:

использовать va_copyудалите алкоголь перед отправкой сообщения

но нет результатов. Я думаю, что проблема связана с этим фактом: va_list не является простым указателем, он может быть чем-то похожим на смещение относительно адреса стека метода. Таким образом, я не могу вызвать objc_msgsend функции (swizzled function) со списком arg другой функции (не swizzled).

Я попытался изменить подход и скопировать все аргументы в NSInvocation, но у меня были другие проблемы с управлением возвращаемым значением вызова, и даже копирование аргументов один за другим (управление всеми различными типами) требует большого количества кода, поэтому я предпочел вернуться для этого подхода, который кажется мне чище (imho)

У вас есть какие-либо предложения? Спасибо

Ответы на вопрос(2)

Ваш ответ на вопрос