Created
November 17, 2012 16:30
-
-
Save enigmaticape/4097340 to your computer and use it in GitHub Desktop.
Wrapping NSInvocation for fun and profit, for values of profit that include a better peformSelector
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import <Foundation/Foundation.h> | |
@interface Invoker : NSObject | |
+ ( NSValue * ) invoke:( SEL ) selector onTarget:( id ) target, ...; | |
@end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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; | |
} | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import <Foundation/Foundation.h> | |
@interface NSObject (invokeSelector) | |
- (NSValue *) invokeSelector:(SEL)selector, ...; | |
@end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import <Foundation/Foundation.h> | |
@interface NSObject (invokeSelector) | |
- (NSValue *) invokeSelector:(SEL)selector, ...; | |
@end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import <Foundation/Foundation.h> | |
@interface TestObj : NSObject | |
-(float) printString:(NSString*) aString | |
andInt:(int) anInt | |
andIncrement:(float) aFloat; | |
@end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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
From a series on things that performSelector probably ought to do but doesn't, and how to get around that in some ways more exciting than using an array. At the Enigmatic Ape Blog http://www.enigmaticape.com/blog/wrapping-nsinvocation-for-fun-and-profit/