可以返回对象或基本类型的函数:是否可能?

时间:2013-08-08 13:13:24

标签: objective-c casting return-value objective-c-runtime

注意:此问题的开头与此问题类似(第一部分相同):LINK
但是,最后一个问题完全不同。

我正在实施一个“Code Injector Class”,通过方法调配可以让你做到这样的事情:

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];

    //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);
}

这里发生了什么?基本上,基于原始选择器的预期返回类型,我使用正确的返回类型向对象添加一个新方法,然后应用swizzle。

一切正常,但我想知道是否可以“压缩”以下代码(一些我不知道的语法或我缺少的东西),因为对于每种返回类型我都有一个与其他函数几乎相同的函数,只有返回的类型不同。 我附上其中两个作为例子:

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;
}

如您所见,函数几乎相同,唯一不同的是返回前的CAST和函数的返回类型。

我是以正确的方式实现它,还是有更有效的方法来实现它? 感谢

1 个答案:

答案 0 :(得分:3)

你是正确的,你需要为每种返回类型编写不同的IMP,至少除非你按照objc_msgSend的方式下载到程序集进行调度。 (尽管这个函数需要几个不同类型的变体。)但是,如果真正的区别仅仅是几个类型名称,那么您可以定义一个减少样板的宏:

// 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...