Question

NOTE: the beginning of this question is similar (the first part is the same) as this one: LINK
However, the final question is completely different.

I'm implementing a "Code Injector Class", that through method swizzling can give you the possibility to do something like this:

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

aSelector can be a method with variable number of arguments, and variable return type. Arguments / and return type can be objects or primitive type.

First, I attach the code of injectCodeBeforeSelector: to let you understand what I'm doing (I removed not interesting parts of the code):

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

    NSString *selector = NSStringFromSelector(method);

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

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

    //NSMethodSignature *signature = [self.mainClass instanceMethodSignatureForSelector:method];

    // 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 originalSelector:method methodTypeEncoding:encoding];
    SwizzleMe(self.mainClass, NSSelectorFromString(selector), NSSelectorFromString(swizzleSelector));

}

    -(void)addSelector:(SEL)selector toClass:(Class)aClass originalSelector:(SEL)originalSel methodTypeEncoding:(const char *)encoding
{

    //NSMethodSignature *signature = [aClass methodSignatureForSelector:originalSel];
    NSMethodSignature *signature = [NSMethodSignature signatureWithObjCTypes:encoding];
    const char *type = [signature methodReturnType];
    IMP implementation = (IMP)intGenericFunction;


    if (strcmp(@encode(id), type) == 0) {
        // the argument is an object
        implementation = objectGenericFunction;
    }
    else if (strcmp(@encode(int), type) == 0)
    {
        // the argument is an int
        implementation = (IMP)intGenericFunction;
    }
    else if (strcmp(@encode(long), type) == 0)
    {
        // the argument is a long
        implementation = (IMP)longGenericFunction;

    }
    else if (strcmp(@encode(double), type) == 0)
    {
        // the argument is double
        implementation = (IMP)doubleGenericFunction;
    }
    else if (strcmp(@encode(float), type) == 0)
    {
        // the argument is float
        implementation = (IMP)floatGenericFunction;
    }
    else
    {
        // the argument is char or others
        implementation = (IMP)intGenericFunction;
    }



    class_addMethod(aClass,
                    selector,
                    implementation, encoding);
}

What is happening here? Basically, basing on the expected return type of the original selector, I add a new method to the object with the correct return type, then apply the swizzle.

All is working correctly, but I'd like to know if it's possible to "compact" the following code (some syntax that I don't know or something I'm missing), because for each return type I have a function that is almost identical to the others, only the returned type is different. I attach two of them as an example:

int intGenericFunction(id self, SEL cmd, ...) {

    FLCodeInjector *injector = [FLCodeInjector injectorForClass:[self class]];
    [injector executeBlockForSelector:cmd];


    va_list arguments, copiedArguments;
    va_start ( arguments, cmd );
    va_copy(copiedArguments, arguments);
    va_end(arguments);

    void * returnValue = getReturnValue(self, cmd, copiedArguments);

    int returnedInt = *(int *)returnValue;
    return returnedInt;
}

double doubleGenericFunction(id self, SEL cmd, ...) {

    FLCodeInjector *injector = [FLCodeInjector injectorForClass:[self class]];
    [injector executeBlockForSelector:cmd];


    va_list arguments, copiedArguments;
    va_start ( arguments, cmd );
    va_copy(copiedArguments, arguments);
    va_end(arguments);

    void * returnValue = getReturnValue(self, cmd, copiedArguments);

    double returnedDouble = *(double *)returnValue;
    return returnedDouble;
}

As you can see, the functions are almost identical, the only different is the CAST before the return, and the return type of the function.

I'm implementing it in the correct way, or there are more efficient way to do it? Thanks

Was it helpful?

Solution

You're correct that you'll need to write a different IMP for each return type, at least unless you drop down to assembly to do the dispatch, the way objc_msgSend does. (Even that function requires a couple different type variants, though.) However, if the difference truly is just a couple of type names, you may be able to define a macro that reduces the boilerplate:

// This macro syntax is off the top of my head; it may not be correct.
#define GENERIC_FUNCTION_FOR_TYPE(type) type type##GenericFunction(id self, SEL cmd, ...) { \
    ...other lines omitted... \
    type returnedValue = *(type *)returnValue; \
    return returnedValue; \
}

GENERIC_FUNCTION_FOR_TYPE(int)
GENERIC_FUNCTION_FOR_TYPE(double)
...etc...
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top