Reputation: 17364
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
Upvotes: 1
Views: 967
Reputation: 17861
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...
Upvotes: 3