Created
November 22, 2012 21:04
-
-
Save enigmaticape/4132914 to your computer and use it in GitHub Desktop.
variadic performSelector, oh yes indeed
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
WARNING : THIS CODE *** DOES NOT WORK *** on OS X 64 bit. | |
Please see the explanatory text at http://www.enigmaticape.com/blog/better-performselector-evil-fun-with-va_args/ for more info in how this works, and why it won't work on OS X 64 bit, yet. |
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
/* For iOS, obvs */ | |
#import "AppDelegate.h" | |
#import "TestObj.h" | |
#import "NSObject+invokeSelector.h" | |
@implementation AppDelegate | |
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions | |
{ | |
TestObj * testobj = [[TestObj alloc] init]; | |
SEL selector | |
= @selector(printString:aBOOL:aBool:aChar:anInt:andIncrement:); | |
float aFloat = 123.456; | |
int anInt = 42; | |
BOOL aBOOL = YES; | |
bool aBool = YES; | |
char aChar = 'z'; | |
NSString * aString = @"The answer is"; | |
NSValue * result; | |
float floatresult; | |
float anotherfloat = 0; | |
NSLog(@"%@ : %@ : %@ : %c : %i : (%f)", | |
aString, | |
aBOOL ? @"YES" : @"NO", | |
aBool ? @"true" : @"fale", | |
aChar, | |
anInt, | |
aFloat | |
); | |
result = [testobj invokeSelector:selector, | |
aString, | |
aBOOL, | |
aBool, | |
aChar, | |
anInt, | |
aFloat | |
]; | |
[result getValue: &floatresult]; | |
NSLog(@"%f", floatresult); | |
[testobj invokeSelector:selector withReturnValue:&anotherfloat, | |
aString, | |
aBOOL, | |
aBool, | |
aChar, | |
anInt, | |
aFloat | |
]; | |
NSLog(@"%f", anotherfloat); | |
return YES; | |
} | |
@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 "NSObject+invokeSelector.h" | |
int main(int argc, const char * argv[]) | |
{ | |
@autoreleasepool { | |
TestObj * testobj = [[TestObj alloc] init]; | |
SEL selector | |
= @selector(printString:aBOOL:aBool:aChar:anInt:andIncrement:); | |
float aFloat = 123.456; | |
int anInt = 42; | |
BOOL aBOOL = YES; | |
bool aBool = YES; | |
char aChar = 'z'; | |
NSString * aString = @"The answer is"; | |
NSValue * result; | |
float floatresult; | |
float anotherfloat = 0; | |
NSLog(@"%@ : %@ : %@ : %c : %i : (%f)", | |
aString, | |
aBOOL ? @"YES" : @"NO", | |
aBool ? @"true" : @"fale", | |
aChar, | |
anInt, | |
aFloat | |
); | |
result = [testobj invokeSelector:selector, | |
aString, | |
aBOOL, | |
aBool, | |
aChar, | |
anInt, | |
aFloat | |
]; | |
[result getValue: &floatresult]; | |
NSLog(@"%f", floatresult); | |
[testobj invokeSelector:selector withReturnValue:&anotherfloat, | |
aString, | |
aBOOL, | |
aBool, | |
aChar, | |
anInt, | |
aFloat | |
]; | |
NSLog(@"%f", anotherfloat); | |
} | |
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, ...; | |
- ( void ) invokeSelector:( SEL )selector withReturnValue:( void* ) retval, ...; | |
@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++ ) { | |
/* we now need to check a few things, | |
firstly whether we have a float | |
*/ | |
if( strcmp( [signature getArgumentTypeAtIndex: 2 + i ], "f") == 0 ) { | |
/* If we do have a float, we need to get the value, | |
and then convert it back to a float ... | |
*/ | |
double doublvalue = va_arg( args, typeof(double) ); | |
float floatvalue = (float)doublvalue; | |
/* ... before we copy it into position | |
*/ | |
[invocation setArgument:&floatvalue atIndex: 2 + i ]; | |
/* Now we increment the pointer past the | |
argument so it points at the next one | |
*/ | |
args += sizeof( double ); | |
} | |
else { | |
/* if it isn't a float, we want to find out | |
what size it is, bearing in mind the whole | |
promotion thingamajig | |
*/ | |
NSUInteger size; | |
NSGetSizeAndAlignment( [signature getArgumentTypeAtIndex: 2 + i ], | |
&size, | |
NULL ); | |
NSUInteger actual_size = size >= 4 ? size : 4; | |
/* since our single byte promoted values will be in | |
the first byte, this should be OK, just pass the | |
pointer and let NSInvocation copy one (or however many) | |
bytes | |
*/ | |
[invocation setArgument:args atIndex: 2 + i ]; | |
/* But we *do* need to make sure we increment the | |
pointer by enough bytes | |
*/ | |
args += actual_size; | |
} | |
} | |
/* and in theory, that should be enough for us | |
to do the invocation | |
*/ | |
[invocation invoke]; | |
va_end( args ); // As far as we know, this is a no op. Hmmm. | |
/* | |
for the moment, we'll just return an NSValue again | |
since there's no particulalrly nice way to return an | |
arbitrary type | |
*/ | |
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; | |
} | |
- ( void ) invokeSelector:( SEL )selector withReturnValue:( void* ) retval, ... { | |
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++ ) { | |
/* we now need to check a few things, | |
firstly whether we have a float | |
*/ | |
if( strcmp( [signature getArgumentTypeAtIndex: 2 + i ], "f") == 0 ) { | |
/* If we do have a float, we need to get the value, | |
and then convert it back to a float ... | |
*/ | |
double doublvalue = va_arg( args, typeof(double) ); | |
float floatvalue = (float)doublvalue; | |
/* ... before we copy it into position | |
*/ | |
[invocation setArgument:&floatvalue atIndex: 2 + i ]; | |
/* Now we increment the pointer past the | |
argument so it points at the next one | |
*/ | |
args += sizeof( double ); | |
} | |
else { | |
/* if it isn't a float, we want to find out | |
what size it is, bearing in mind the whole | |
promotion thingamajig | |
*/ | |
NSUInteger size; | |
NSGetSizeAndAlignment( [signature getArgumentTypeAtIndex: 2 + i ], | |
&size, | |
NULL ); | |
NSUInteger actual_size = size >= 4 ? size : 4; | |
/* since our single byte promoted values will be in | |
the first byte, this should be OK, just pass the | |
pointer and let NSInvocation copy one (or however many) | |
bytes | |
*/ | |
[invocation setArgument:args atIndex: 2 + i ]; | |
/* But we *do* need to make sure we increment the | |
pointer by enough bytes | |
*/ | |
args += actual_size; | |
} | |
} | |
/* and in theory, that should be enough for us | |
to do the invocation | |
*/ | |
[invocation invoke]; | |
va_end( args ); // As far as we know, this is a no op. Hmmm. | |
/* | |
OK, this time, we'll copy the value straight out, like this, | |
and NSValue is all gone. | |
*/ | |
if ( [signature methodReturnLength] > 0 ) { | |
[invocation getReturnValue:retval]; | |
} | |
} | |
@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, ...; | |
- ( void ) invokeSelector:( SEL )selector withReturnValue:( void* ) retval, ...; | |
@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++ ) { | |
/* we now need to check a few things, | |
firstly whether we have a float | |
*/ | |
if( strcmp( [signature getArgumentTypeAtIndex: 2 + i ], "f") == 0 ) { | |
/* If we do have a float, we need to get the value, | |
and then convert it back to a float ... | |
*/ | |
double doublvalue = va_arg( args, typeof(double) ); | |
float floatvalue = (float)doublvalue; | |
/* ... before we copy it into position | |
*/ | |
[invocation setArgument:&floatvalue atIndex: 2 + i ]; | |
/* Now we increment the pointer past the | |
argument so it points at the next one | |
*/ | |
args += sizeof( double ); | |
} | |
else { | |
/* if it isn't a float, we want to find out | |
what size it is, bearing in mind the whole | |
promotion thingamajig | |
*/ | |
NSUInteger size; | |
NSGetSizeAndAlignment( [signature getArgumentTypeAtIndex: 2 + i ], | |
&size, | |
NULL ); | |
NSUInteger actual_size = size >= 4 ? size : 4; | |
/* since our single byte promoted values will be in | |
the first byte, this should be OK, just pass the | |
pointer and let NSInvocation copy one (or however many) | |
bytes | |
*/ | |
[invocation setArgument:args atIndex: 2 + i ]; | |
/* But we *do* need to make sure we increment the | |
pointer by enough bytes | |
*/ | |
args += actual_size; | |
} | |
} | |
/* and in theory, that should be enough for us | |
to do the invocation | |
*/ | |
[invocation invoke]; | |
va_end( args ); // As far as we know, this is a no op. Hmmm. | |
/* | |
for the moment, we'll just return an NSValue again | |
since there's no particulalrly nice way to return an | |
arbitrary type | |
*/ | |
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; | |
} | |
- ( void ) invokeSelector:( SEL )selector withReturnValue:( void* ) retval, ... { | |
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++ ) { | |
/* we now need to check a few things, | |
firstly whether we have a float | |
*/ | |
if( strcmp( [signature getArgumentTypeAtIndex: 2 + i ], "f") == 0 ) { | |
/* If we do have a float, we need to get the value, | |
and then convert it back to a float ... | |
*/ | |
double doublvalue = va_arg( args, typeof(double) ); | |
float floatvalue = (float)doublvalue; | |
/* ... before we copy it into position | |
*/ | |
[invocation setArgument:&floatvalue atIndex: 2 + i ]; | |
/* Now we increment the pointer past the | |
argument so it points at the next one | |
*/ | |
args += sizeof( double ); | |
} | |
else { | |
/* if it isn't a float, we want to find out | |
what size it is, bearing in mind the whole | |
promotion thingamajig | |
*/ | |
NSUInteger size; | |
NSGetSizeAndAlignment( [signature getArgumentTypeAtIndex: 2 + i ], | |
&size, | |
NULL ); | |
NSUInteger actual_size = size >= 4 ? size : 4; | |
/* since our single byte promoted values will be in | |
the first byte, this should be OK, just pass the | |
pointer and let NSInvocation copy one (or however many) | |
bytes | |
*/ | |
[invocation setArgument:args atIndex: 2 + i ]; | |
/* But we *do* need to make sure we increment the | |
pointer by enough bytes | |
*/ | |
args += actual_size; | |
} | |
} | |
/* and in theory, that should be enough for us | |
to do the invocation | |
*/ | |
[invocation invoke]; | |
va_end( args ); // As far as we know, this is a no op. Hmmm. | |
/* | |
OK, this time, we'll copy the value straight out, like this, | |
and NSValue is all gone. | |
*/ | |
if ( [signature methodReturnLength] > 0 ) { | |
[invocation getReturnValue:retval]; | |
} | |
} | |
@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 | |
aBOOL:(BOOL) aBOOL | |
aBool:(bool) aBool | |
aChar:(char) aChar | |
anInt:(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 | |
aBOOL:(BOOL) aBOOL | |
aBool:(bool) aBool | |
aChar:(char) aChar | |
anInt:(int) anInt | |
andIncrement:(float) aFloat | |
{ | |
NSLog(@"%@ : %@ : %@ : %c : %i : (%f)", | |
aString, | |
aBOOL ? @"YES" : @"NO", | |
aBool ? @"true" : @"false", | |
aChar, | |
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 short series on doing mo better performSelector type stuff, explanatory post on the Enigmatic Ape blog at http://www.enigmaticape.com/blog/better-performselector-evil-fun-with-va_args/