C object using a string to call a method

Hi, new to lens C, and wondered if anyone could help me with this. I have several different methods, each of which requires 3 input values ​​and usually calls it with

[self methodA:1 height:10 speed:3] 

but the name of the method that I want to read from the string in plist, for example, if the string was methodB, I would get

 [self methodB:1 height:10 speed:3] 

for "methodC"

 [self methodC:1 height:10 speed:3] 

etc.

Any ideas how I can do this, I tried to define the string as a selector using NSSelectorFromString

 NSString *string = [plistA objectForKey:@"method"]; SEL select = NSSelectorFromString(string); [self performSelector:select:c height:b speed:a]; 

However, this did not work, any help would be greatly appreciated. We tried the solution below, but could not work, that’s what I tried.

So, just to repeat, I have methods like

  spawnEnemyA:2 withHeight:3 withSpeed:4 spawnEnemyB:3 withHeight:2 withSpeed:5 

and I want to read the values ​​that I want to pass to these methods, as well as the type of method from the plist file. my code is as follows: //////////////////////////////////////////// //////////////////

// These are the values ​​I read from plist that I want my method to use

  int a = [[enemySettings objectForKey:@"speed"] intValue]; int b = [[enemySettings objectForKey:@"position"] intValue]; int c = [[enemySettings objectForKey:@"delay"] intValue]; // I Also read the method name from the plist and combine it into a single string NSString *method = [enemySettings objectForKey:@"enemytype"]; NSString *label1 = @"spawn"; NSString *label2 = @":withHeight:withSpeed:"; NSString *combined = [NSString stringWithFormat:@"%@%@%@",label1, method,label2]; //Check that the string is correct get spawnEnemyA:withHeight:withSpeed: CCLOG(@"%@",combined); //This is the Invocation part NSInvocation * invocation = [ NSInvocation new ]; [ invocation setSelector: NSSelectorFromString(combined)]; [ invocation setArgument: &c atIndex: 2 ]; [ invocation setArgument: &b atIndex: 3 ]; [ invocation setArgument: &a atIndex: 4 ]; [ invocation invokeWithTarget:self ]; [invocation release ]; 

//////////////////////////////////////////////////// //////////////////

The code compiles without errors, but the methods are not called. Any ideas? Greetings

+4
source share
4 answers

You cannot use performSelector for a method with 3 (or more) arguments.
But for your information here, how to use it:

 SEL m1; SEL m2; SEL m3; m1 = NSSelectorFromString( @"someMethodWithoutArg" ); m2 = NSSelectorFromString( @"someMethodWithAnArg:" ); m1 = NSSelectorFromString( @"someMethodWithAnArg:andAnotherOne:" ); [ someObject performSelector: m1 ]; [ someObject performSelector: m2 withObject: anArg ]; [ someObject performSelector: m2 withObject: anArg withObject: anOtherArg ]; 

For methods with more than two arguments, you will need to use the NSInvocation class .
Take a look at the documentation to find out how to use it.

Basically:

 NSInvocation * invocation = [ NSInvocation new ]; [ invocation setSelector: NSStringFromSelector( @"methodWithArg1:arg2:arg3:" ) ]; // Argument 1 is at index 2, as there is self and _cmd before [ invocation setArgument: &arg1 atIndex: 2 ]; [ invocation setArgument: &arg2 atIndex: 3 ]; [ invocation setArgument: &arg3 atIndex: 4 ]; [ invocation invokeWithTarget: targetObject ]; // If you need to get the return value [ invocation getReturnValue: &someVar ]; [ invocation release ]; 
+11
source

In general, this type of dynamism often indicates an anti-pattern. A data column with an implementation in this way is usually not the best practice.

Sometimes, however, this is necessary. If you go this route, given that your various method declarations may look like this:

 - (void)methodAWidth:(NSUInteger)w height:(NSUInteger)h speed:(NSUInteger)s; - (void)methodBWidth:(NSUInteger)w height:(NSUInteger)h speed:(NSUInteger)s; - (void)methodCWidth:(NSUInteger)w height:(NSUInteger)h speed:(NSUInteger)s; 

You probably need something like:

 NSString *selName = [NSString stringWithFormat:@"method%@Width:height:speed:", ... one of @"A", @"B", or @"C" ....]; SEL selector = NSelectorFromString(selName); 

Then:

 if (![target respondsToSelector:selector]) return; // no can do void (*castMsgSend)(id, SEL, NSUInteger, NSUInteger, NSUInteger) = (void*)objc_msgSend; castMsgSend(target, selector, 1, 10, 3); 

Each method call is compiled before objc_msgSend() called. By doing this, you create a completely secure site type / type that goes through the regular Objective-C messaging engine, but the selector is dynamically determined on the fly. \

While performSelector: (and variants with multiple arguments) are convenient, they cannot deal with non-object types.


And, as McMade noted in a comment, keep an eye on floating point returns and structure. They use different variants of objc_msgSend (), which the compiler automatically processes in the usual case [foo bar] .

+4
source

You can directly use objc_msgsend :

 NSString *methodName = [plistA objectForKey:@"method"]; objc_msgSend(self, methodName, c, b, a); 

Remember that the selector must include all parts, for example @"method:height:speed:"

+1
source

You should replace the following line:

 [self performSelector:select:c height:b speed:a]; 

and write the following:

 [self performSelector:select withObject:[NSArray arrayWithObjects:c,b,a,nil]]; 
-1
source

All Articles