Skip to content

Instantly share code, notes, and snippets.

@enigmaticape
Created November 14, 2012 19:59
Show Gist options
  • Save enigmaticape/4074403 to your computer and use it in GitHub Desktop.
Save enigmaticape/4074403 to your computer and use it in GitHub Desktop.
You want to use NSObject performSelector with multiple parameters, but you can't ? (1)
- ( id ) methodWithOneParam:( id ) theParam {
// Do amazing stuff
return @"Srsly, Amazing!";
}
- ( id ) methodWithFirst:( id ) firstParam
andSecond:( id ) secondParam
{
// Do doubly amazing stuff
return @"Even Amazinger";
}
- ( id ) methodWithFirst:( id ) firstParam
andSecond:( id ) secondParam
andThird:( id ) thirdParam
{
// Do thricely amazing stuff that will certainly rock.
return @"MOAR AMAZINGER-ER!";
}
id first = nil;
id second = nil;
id third = nil;
id result = nil;
// easy peasy to do the first one.
SEL singleParamSelector = @selector(methodWithOneParam:);
result = [self performSelector:singleParamSelector // https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Protocols/NSObject_Protocol/Reference/NSObject.html
withObject:first];
NSLog(@"%@", result);
/*
Let's get a bit trickier and get the selector signature
from a string this tine
*/
SEL doubleParamSelector
= NSSelectorFromString(@"methodWithFirst:andSecond:");
result = [self performSelector: doubleParamSelector
withObject: first
withObject: second];
NSLog(@"%@", result);
- ( id ) boringMethod:( NSDictionary *) args {
id first = [args objectForKey:@"first" ];
id second = [args objectForKey:@"second"];
id third = [args objectForKey:@"third" ];
return @"Whatever";
}
NSInvocation * invocation; // https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/DistrObjects/Tasks/invocations.html#//apple_ref/doc/uid/20000744-CJBBACJH
NSMethodSignature * methSig; // http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSMethodSignature_Class/Reference/Reference.html
SEL triParamSelector;
/*
We need both a selector and a method signature,
they are different. A method signature contains
information about the number and types of arguments
a method takes, whereas a selector is just a name.
*/
triParamSelector = @selector(methodWithFirst:andSecond:andThird:);
methSig = [self methodSignatureForSelector: triParamSelector];
invocation = [NSInvocation invocationWithMethodSignature: methSig];
/*
Set the target (the object to invoke the
selector on) and the selector to invoke
*/
[invocation setSelector: triParamSelector];
[invocation setTarget: self];
/*
Now we have to set up the arguments. Note that we
have to start from 2, because there are two 'hidden'
arguments, which we'll get to shortly
*/
[invocation setArgument: &first atIndex: 2];
[invocation setArgument: &second atIndex: 3];
[invocation setArgument: &third atIndex: 4];
/*
Now make the call and get the returned value.
*/
[invocation invoke];
[invocation getReturnValue: &result];
NSLog(@"NSInvocation : %@", result);
SEL betterWaySelector
= NSSelectorFromString(@"methodWithFirst:andSecond:andThird:");
IMP methodImplementation // https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSObject_Class/Reference/Reference.html
= [self methodForSelector: betterWaySelector];
result = methodImplementation( self,
betterWaySelector,
first,
second,
third );
NSLog(@"methodForSelector : %@", result);
SEL evenBetterWay
= NSSelectorFromString(@"methodWithFirst:andSecond:andThird:");
result = objc_msgSend( self,
evenBetterWay,
first,
second,
third );
NSLog(@"objc_msgSend : %@", result);
- ( void ) beware:( float ) whoops {
NSLog(@"%f", whoops);
}
- ( float ) incrementFloat:( float ) digits {
return digits + 1.0;
}
- ( void ) carefulNow {
SEL selector = @selector(beware:);
IMP method = [self methodForSelector: selector];
float test = 123.456;
/* works, obvs */
[self beware: test];
/*
123.456001
*/
/* bork */
objc_msgSend( self, selector, test);
/*
0.000000
*/
/* bork */
method( self, selector, test );
/*
0.000000
*/
/* works */
((void (*) (id, SEL,float))method)(self,selector,test);
/*
123.456001
*/
/* works */
((void (*) (id, SEL, float))objc_msgSend)(self,selector,test);
/*
123.456001
*/
selector = @selector(incrementFloat:);
float result
= ((float (*)(id,SEL,float))objc_msgSend)(self,selector,test);
NSLog(@"%f", result);
/*
124.456001
*/
method = [self methodForSelector:selector];
result = ((float (*) (id, SEL, float))method)(self,selector,test);
NSLog(@"%f", result);
/*
124.456001
*/
}
@enigmaticape
Copy link
Author

@matt-curtis
Copy link

Reading this was... intense. Thanks for the overview! :)

@alexshen
Copy link

alexshen commented Feb 2, 2020

Cool, thanks for sharing. By the way, the blog link seems broken.

One thing to notice when you call getReturnValue to get an object under ARC is that you need to either qualify result with __unsafe_unretained or change result to void* and __bridge cast result, otherwise the program may crash. See NSInvocation getReturnValue: called inside forwardInvocation: makes the returned object call dealloc:
.

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