Skip to content

Instantly share code, notes, and snippets.

@123nobody
Forked from enigmaticape/Invoker.h
Created March 26, 2014 10:16
Show Gist options
  • Save 123nobody/9780273 to your computer and use it in GitHub Desktop.
Save 123nobody/9780273 to your computer and use it in GitHub Desktop.
#import <Foundation/Foundation.h>
@interface Invoker : NSObject
+ ( NSValue * ) invoke:( SEL ) selector onTarget:( id ) target, ...;
@end
#import "Invoker.h"
@implementation Invoker
+ ( NSValue * ) invoke:( SEL ) selector onTarget:( id ) target, ... {
/* get set up to use a variadic argument list */
va_list args;
va_start(args, target);
/* Do the standard NSInvocation setup */
NSMethodSignature * signature
= [target methodSignatureForSelector:selector];
NSInvocation * invocation
= [NSInvocation invocationWithMethodSignature:signature];
[invocation setTarget: target];
[invocation setSelector:selector];
/* Now then, here starts the fun part,
get a count of how many arguments to expect, information
which is encapsulated by the method signature.
*/
NSUInteger arg_count = [signature numberOfArguments];
for( NSInteger i = 0; i < arg_count - 2; i++ ) {
/*
get an id value from the argument list.
*/
id arg = va_arg(args, id);
/*
if it is an NSValue, or subclass thereof, we have some extra
work to do before we can pass it on to the invocation
*/
if( [arg isKindOfClass:[NSValue class]] ) {
NSUInteger arg_size;
/*
NSGetSizeAndAlignment will give us a size in bytes
based on the type information that the runtime has stored,
which is accessed (in NSValue's case) by calling
[NSValue objCtype], 4 for an it, 1 for a char and so on.
*/
NSGetSizeAndAlignment([arg objCType], &arg_size, NULL);
/*
We can't (AFAIK) directly access NSValue's bytes,
so we'll need a temporary buffer to read the value into.
It's possible that we could simply use a large buffer and
rely on NSInvocation to know how much to copy, thus obviating
the need for multiple malloc()s, but I haven't tested that yet
*/
void * arg_buffer = malloc(arg_size);
/* copy the value, obvs
*/
[arg getValue:arg_buffer];
/*
Now we pass the buffer to NSInvocation, which copies it.
NB that we start at index 2, because index 0 and 1 are
the id and SEL of the targeted method
*/
[invocation setArgument:arg_buffer atIndex: 2 + i ];
free(arg_buffer);
}
else {
/*
If we have a non NSValue argument, just copy it
*/
[invocation setArgument:&arg atIndex: 2 + i];
}
}
/*
indicate that we are done with the variadic argument list
*/
va_end(args);
/*
do the bidniss
*/
[invocation invoke];
/*
Of course now, we have to get the value back out.
There are a couple f ways to do thsi, let's stick
with NSValue, since we're on such close terms.
*/
NSValue * ret_val = nil;
NSUInteger ret_size = [signature methodReturnLength];
if( ret_size > 0 ) {
void * ret_buffer = malloc( ret_size );
[invocation getReturnValue:ret_buffer];
ret_val = [NSValue valueWithBytes:ret_buffer
objCType:[signature methodReturnType]];
free(ret_buffer);
}
return ret_val;
}
@end
#import <Foundation/Foundation.h>
#import "TestObj.h"
#import "Invoker.h"
#import "NSObject+invokeSelector.h"
int main(int argc, const char * argv[])
{
@autoreleasepool {
TestObj * testobj = [[TestObj alloc] init];
SEL selector = @selector(printString:andInt:andIncrement:);
float aFloat = 123.456;
int anInt = 42;
NSString * aString = @"The answer is";
NSValue * result;
float floatresult;
result = [Invoker invoke:selector onTarget:testobj,
aString,
[NSNumber numberWithInt:anInt],
[NSNumber numberWithFloat:aFloat]
];
[result getValue: &floatresult];
NSLog(@"%f", floatresult);
/* And now with the NSObject category */
result = [testobj invokeSelector:selector,
aString,
[NSNumber numberWithInt:anInt],
[NSNumber numberWithFloat:aFloat]
];
[result getValue: &floatresult];
NSLog(@"%f", floatresult);
// Really, still no ARC, tsk tsk, Steve.
[testobj release];
}
return 0;
}
#import <Foundation/Foundation.h>
@interface NSObject (invokeSelector)
- (NSValue *) invokeSelector:(SEL)selector, ...;
@end
#import "NSObject+invokeSelector.h"
@implementation NSObject (invokeSelector)
- (NSValue *) invokeSelector:(SEL)selector, ... {
va_list args;
va_start(args, selector);
NSMethodSignature * signature
= [self methodSignatureForSelector:selector];
NSInvocation * invocation
= [NSInvocation invocationWithMethodSignature:signature];
[invocation setTarget:self];
[invocation setSelector:selector];
NSUInteger arg_count = [signature numberOfArguments];
for( NSInteger i = 0; i < arg_count - 2; i++ ) {
id arg = va_arg(args, id);
if( [arg isKindOfClass:[NSValue class]] ) {
NSUInteger arg_size;
NSGetSizeAndAlignment([arg objCType], &arg_size, NULL);
void * arg_buffer = malloc(arg_size);
[arg getValue:arg_buffer];
[invocation setArgument:arg_buffer atIndex: 2 + i ];
free(arg_buffer);
}
else {
[invocation setArgument:&arg atIndex: 2 + i];
}
}
va_end(args);
[invocation invoke];
NSValue * ret_val = nil;
NSUInteger ret_size = [signature methodReturnLength];
if( ret_size > 0 ) {
void * ret_buffer = malloc( ret_size );
[invocation getReturnValue:ret_buffer];
ret_val = [NSValue valueWithBytes:ret_buffer
objCType:[signature methodReturnType]];
free(ret_buffer);
}
return ret_val;
}
@end
#import <Foundation/Foundation.h>
@interface NSObject (invokeSelector)
- (NSValue *) invokeSelector:(SEL)selector, ...;
@end
#import "NSObject+invokeSelector.h"
@implementation NSObject (invokeSelector)
- (NSValue *) invokeSelector:(SEL)selector, ... {
va_list args;
va_start(args, selector);
NSMethodSignature * signature
= [self methodSignatureForSelector:selector];
NSInvocation * invocation
= [NSInvocation invocationWithMethodSignature:signature];
[invocation setTarget:self];
[invocation setSelector:selector];
NSUInteger arg_count = [signature numberOfArguments];
for( NSInteger i = 0; i < arg_count - 2; i++ ) {
id arg = va_arg(args, id);
if( [arg isKindOfClass:[NSValue class]] ) {
NSUInteger arg_size;
NSGetSizeAndAlignment([arg objCType], &arg_size, NULL);
void * arg_buffer = malloc(arg_size);
[arg getValue:arg_buffer];
[invocation setArgument:arg_buffer atIndex: 2 + i ];
free(arg_buffer);
}
else {
[invocation setArgument:&arg atIndex: 2 + i];
}
}
va_end(args);
[invocation invoke];
NSValue * ret_val = nil;
NSUInteger ret_size = [signature methodReturnLength];
if( ret_size > 0 ) {
void * ret_buffer = malloc( ret_size );
[invocation getReturnValue:ret_buffer];
ret_val = [NSValue valueWithBytes:ret_buffer
objCType:[signature methodReturnType]];
free(ret_buffer);
}
return ret_val;
}
@end
#import <Foundation/Foundation.h>
@interface TestObj : NSObject
-(float) printString:(NSString*) aString
andInt:(int) anInt
andIncrement:(float) aFloat;
@end
#import "TestObj.h"
@implementation TestObj
-(float) printString:(NSString*) aString
andInt:(int) anInt
andIncrement:(float) aFloat
{
NSLog(@"%@ : %i : (%f)", aString, anInt, aFloat);
return aFloat + 1.0;
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment