Skip to content

Instantly share code, notes, and snippets.

@enigmaticape
Created November 22, 2012 21:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save enigmaticape/4132914 to your computer and use it in GitHub Desktop.
Save enigmaticape/4132914 to your computer and use it in GitHub Desktop.
variadic performSelector, oh yes indeed
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.
/* 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
#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;
}
#import <Foundation/Foundation.h>
@interface NSObject (invokeSelector)
- (NSValue *) invokeSelector:(SEL)selector, ...;
- ( void ) invokeSelector:( SEL )selector withReturnValue:( void* ) retval, ...;
@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++ ) {
/* 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
#import <Foundation/Foundation.h>
@interface NSObject (invokeSelector)
- (NSValue *) invokeSelector:(SEL)selector, ...;
- ( void ) invokeSelector:( SEL )selector withReturnValue:( void* ) retval, ...;
@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++ ) {
/* 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
#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
#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
@enigmaticape
Copy link
Author

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/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment