Reputation: 2699
In MyClass.m, I've defined
- (void) myTest: (NSString *) withAString{
NSLog(@"hi, %@", withAString);
}
and the appropriate declaration in MyClass.h . Later I want to call
[self performSelector:@selector(mytest:withAString:) withObject: mystring];
in MyClass.m but I get an error similar to * Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '* -[MyClass myTest:withAtring:]: unrecognized selector sent to instance 0xe421f0'
I tried a simpler case with a selector that took no arguments that printed a string to console and that worked just fine. What's wrong with the code and how can I fix it? Thanks.
Upvotes: 150
Views: 223364
Reputation: 39
The correct syntax is
[self performSelector: @selector(mytest:) withObject: mystring];
I see a comment here regarding how one can't pass arguments other than objects, as well as how Apple limited to two. You can use the methodForSelector
method to bypass these limitations.
SEL sel = @selector(mytest:);
(*[self methodForSelector: sel])(self, sel, mystring);
Dereferencing is optional. Therefore,
[self methodForSelector: sel](self, sel, mystring);
is valid as well.
Upvotes: 0
Reputation: 4344
In Objective-C, a selector's signature consists of:
Selectors have no knowledge of:
Here's a class implementation where performMethodsViaSelectors method performs the other class methods by way of selectors:
@implementation ClassForSelectors
- (void) fooNoInputs {
NSLog(@"Does nothing");
}
- (void) fooOneInput:(NSString*) first {
NSLog(@"Logs %@", first);
}
- (void) fooFirstInput:(NSString*) first secondInput:(NSString*) second {
NSLog(@"Logs %@ then %@", first, second);
}
- (void) performMethodsViaSelectors {
[self performSelector:@selector(fooNoInputs)];
[self performSelector:@selector(fooOneInput:) withObject:@"first"];
[self performSelector:@selector(fooFirstInput:secondInput:) withObject:@"first" withObject:@"second"];
}
@end
The method you want to create a selector for has a single input, so you would create a selector for it like so:
SEL myTestSelector = @selector(myTest:);
Upvotes: 316
Reputation: 15
(OC is harder to use, -_-||)
Calling selectors with multiple arguments,
you can use NSObject performSelector:withObject:withObject
,
but only support pass two arguments!!!
Fortunately, you can implement you performSelector withObject X 3
by objc_msgSend
function.
#include <objc/message.h>
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2 withObject:(id)object3 {
typedef id (*send_type)(id, SEL, id, id, id);
send_type func = (send_type) objc_msgSend;
id retValue = func(self, aSelector, object1, object2, object3);
return retValue;
}
Usage:
- (NSString *)ObjcMsgSendWithString:(NSString *)string withNum:(NSNumber *)number withArray:(NSArray *)array {
NSLog(@" ---> %@, %@, %@", string, number, array[0]);
return @"return 311";
}
- (void)test{
NSString *str = @"字符串objc_msgSend";
NSNumber *num = @20;
NSArray *arr = @[@"数组值1", @"数组值2"];
SEL sel = @selector(ObjcMsgSendWithString:withNum:withArray:);
NSLog(@"1223 ---> %@", [self performSelector:sel withObject:str withObject:num withObject:arr]);
}
Upvotes: 0
Reputation: 1806
iOS users also expect autocapitalization: In a standard text field, the first letter of a sentence in a case-sensitive language is automatically capitalized.
You can decide whether or not to implement such features; there is no dedicated API for any of the features just listed, so providing them is a competitive advantage.
Apple document is saying there is no API available for this feature and some other expected feature in a customkeyboard. so you need to find out your own logic to implement this.
Upvotes: -1
Reputation: 3187
@Shane Arney
performSelector:withObject:withObject:
You might also want to mention that this method is only for passing maximum 2 arguments, and it cannot be delayed. (such as performSelector:withObject:afterDelay:)
.
kinda weird that apple only supports 2 objects to be send and didnt make it more generic.
Upvotes: 14
Reputation: 5384
Your method signature is:
- (void) myTest:(NSString *)
withAString happens to be the parameter (the name is misleading, it looks like it is part of the selector's signature).
If you call the function in this manner:
[self performSelector:@selector(myTest:) withObject:myString];
It will work.
But, as the other posters have suggested, you may want to rename the method:
- (void)myTestWithAString:(NSString*)aString;
And call:
[self performSelector:@selector(myTestWithAString:) withObject:myString];
Upvotes: 138
Reputation: 1254
Your code has two problems. One was identified and answered, but the other wasn't. The first was that your selector was missing the name of its parameter. However, even when you fix that, the line will still raise an exception, assuming your revised method signature still includes more than one argument. Let's say your revised method is declared as:
-(void)myTestWithString:(NSString *)sourceString comparedTo:(NSString *)testString ;
Creating selectors for methods that take multiple arguments is perfectly valid (e.g. @selector(myTestWithString:comparedTo:) ). However, the performSelector method only allows you to pass one value to myTest, which unfortunately has more than one parameter. It will error out and tell you that you didn't supply enough values.
You could always redefine your method to take a collection as it's only parameter:
-(void)myTestWithObjects:(NSDictionary *)testObjects ;
However, there is a more elegant solution (that doesn't require refactoring). The answer is to use NSInvocation, along with its setArgument:atIndex:
and invoke
methods.
I've written up an article, including a code example, if you want more details. The focus is on threading, but the basics still apply.
Good luck!
Upvotes: 7
Reputation: 299355
Your method signature makes no sense, are you sure it isn't a typo? I'm not clear how it's even compiling, though perhaps you're getting warnings that you're ignoring?
How many parameters do you expect this method to take?
Upvotes: 3
Reputation: 9796
Think the class should be defined as:
- (void) myTestWithSomeString:(NSString *) astring{
NSLog(@"hi, %s", astring);
}
You only have a single parameter so you should only have a single :
You might want to consider using %@ in your NSLog also - it is just a good habit to get into - will then write out any object - not just strings.
Upvotes: 2