Objective-C: How to perform a performSelector:#selector? - objective-c

Well, I'm creating a custom SEL like:
NSArray *tableArray = [NSArray arrayWithObjects:#"aaa", #"bbb", nil];
for ( NSString *table in tableArray ){
SEL customSelector = NSSelectorFromString([NSString stringWithFormat:#"abcWith%#", table]);
[self performSelector:customSelector withObject:0];
}
I got a error:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Sync aaaWithaaa]: unrecognized selector sent to instance
but if i run it with the real method name it works!
[self performSelector:#selector(aaaWithaaa:) withObject:0];
How to solve it out?

You've already created selector from string - pass it to performSelector: method:
[self performSelector:customSelector withObject:0];
Edit: Mind, that if your method takes parameter then you must use colon when create selector from it:
// Note that you may need colon here:
[NSString stringWithFormat:#"abcWith%#:", table]

NSArray *tableArray = [NSArray arrayWithObjects:#"aaa", #"bbb", nil];
for ( NSString *table in tableArray ){
SEL customSelector = NSSelectorFromString([NSString stringWithFormat:#"abcWith%#:", table]);
[self performSelector:customSelector withObject:0];
}

- (id)performSelector:(SEL)aSelector withObject:(id)anObject
First argument is SEL type.
SEL customSelector = NSSelectorFromString([NSString stringWithFormat:#"abcWith%#", table]);
[self performSelector:customSelector withObject:0];

Close.
The difference is that with #selector(aaaWithaaa:) you pass a method name but with #selector(customSelector:) you're passing a variable of type SEL (with a spare colon).
Instead, you just need:
[self performSelector:customSelector withObject:0];
The other difference is that you write your string with a colon at the end, but you stringWithFormat: has none. It's important; it means that the method takes a parameter. If your method has a parameter, it needs to be there, i.e.,
[NSString stringWithFormat:#"abcWith%#:", table]

Related

Why does this code break at removeObject:?

+ (NSString *)numberMatching: (NSString *)number and: (NSString *)secondNumber
{
NSString *returnNumber;
if ([number isEqualToString:secondNumber]) {
returnNumber = number;
} else {
NSMutableArray *validNumber = [[self validNumbers] copy];
[validNumber removeObject:number];
[validNumber removeObject:secondNumber];
returnNumber = validNumber[0];
}
return returnNumber;
}
In case it matters, here is the code for validNumber:
+ (NSArray *)validNumbers
{
static NSArray *validNumbers = nil;
if (!validNumbers) validNumbers = #[#"one",#"two",#"three"];
return validNumbers;
}
When run, I get the following error:
-[_NSArrayI removeObject:]: unrecognized selector sent to instance 0x1ed60f00 ... Terminating app due to uncaught exception
'NSInvalidArgumentException', reason: '-[_NSArrayI removeObject:]:
unrecognized selector sent to instance 0x1ed60f00'
That would seem to indicate that removeObject is not a valid method for an NSMutableArray, but it is valid.
Essentially, what I want to do is this: if the two arguments match, I want to return the value. If they don't match, I want to return the third possibility (doesn't match either argument).
Because validNumber is an NSArray. You can't remove (or add) objects from an NSArray. Use NSMutableArray and mutableCopy instead:
NSMutableArray *validNumber = [[self validNumbers] mutableCopy];
[validNumber removeObject:number];
[validNumber removeObject:secondNumber];
When you send the copy message to an NSArray, the copy you get is also an NSArray instance and you can't remove objects from an immutable array. In order to get a mutable copy you need to send the mutableCopy message:
NSMutableArray *validNumber = [[self validNumbers] mutableCopy];

[__NSCFString superview]: unrecognized selector sent to instance

I am trying to copy a NSMutableArray* into NSArray*, but it does not works and it generates a [__NSCFString superview]: unrecognized selector sent to instance error . Here is the code:
//where gc is a NSDictionary*, recentKey is a NSString*, and _objects is a NSArray*
//gc is an singleton is used to save chache data, with NSKeyedUnarchiver class
//_objects is used to reload the UITableView's data
NSArray *savedNews = [[NSArray alloc] initWithArray:[gc objectForKey:recentkey]];
//this not works...why??
_objects = [[NSArray alloc] initWithArray:savedNews];
Resolution:
Yes, as Herman suggests, the error was external. The savedNews Array was using a class with NSEncoding with an error:
- (void)encodeWithCoder:(NSCoder *)encoder {
//...where element was NSString* and not "UIImageView"
// element should be imgView
if (imgView) [encoder encodeObject:element forKey:#"imgView"];
}
Thanks for all guys.
Somewhere in your App is the superview of an NSString object fetched.
I guess that you assigned an NSString object to something where a UIView is expected.
Could be something like:
someButton.textLabel = someString; // wrong - but should generate a compiler warning
instead of
someButton.textLabel.text = someString; // correct
This is not directly related to your array issue.
First, check what the object in the dictionary is for that key.
NSLog(#"GC Object type for recentkey:%#", [[gc objectForKey:recentkey] class]);
You can only pass an NSArray to initWithArray:
So if that object isn't already an NSArray but you want that object to be IN an array. The do this..
id obj = [gc objectForKey:recentkey]; //Because I have no idea what this is
NSArray *savedNews = [NSArray arrayWithObject:obj];

Too many arguments to method call

I am trying to set the initial text for what the twitter message should say in my app using a NSString from my appDelegate. Check out the code here:
NSString *tweet;
tweet=[MyWebFunction tweet:appDelegate.stadium_id];
if([deviceType hasPrefix:#"5."]){
// Create the view controller
TWTweetComposeViewController *twitter = [[TWTweetComposeViewController alloc] init];
[twitter setInitialText:#"#%",tweet];
The problem is, is there is an error at the twitter setInitialText that there are Too many arguments to method call, expected 1, have 2. ?!?!?
Any help is greatly appreciated. :)
The TWTweetComposeViewController method setInitialText only takes one argument, being of type NSString*. You cannot simply format any and all NSString variables passed to a method as you can with the NSString method stringWithFormat (which is, I imagine, where you've seen the syntax [NSString stringWithFormat:#"%#", myString]).
In your case, you either need to simply call:
[twitter setInitialText:tweet];
or call:
[twitter setInitialText:[NSString stringWithFormat:#"%#", tweet]]
EDIT
I feel it necessary to add, to further your understanding, that a method only takes a variable number of arguments (such as stringWithFormat) when its declaration ends with ...
For example, looking in the docs for NSString reveals that stringWithFormat is declared as such:
+(id) stringWithFormat:(NSString *)format, ...;
Similarly, arrayWithObjects in NSArray is declared as such:
+(id) arrayWithObjects:(id)firstObj, ...;
which one would use like:
NSString* myString1 = #"foo";
NSString* myString2 = #"bar";
NSNumber* myNumber = [NSNumber numberWithInt:42];
NSArray* myArray = [NSArray arrayWithObjects:myString1, myString2, myNumber, nil];
Try [twitter setInitialText:tweet];
If you really need formatted text for a more complex case, try
[twitter setInitialText:[NSString stringWithFormat:#"%#", tweet]];
"[twitter setInitialText:#"#%",tweet];"
you just got your "#" and your "%" the wrong way round it should be
[twitter setInitialText:#"**%#**",tweet];

Can't have an ivar with a name of description in Objective-C description method?

I'm trying to implement the Objective-C description method for my NSObject-derived object.
However, my derived object has an ivar of name description. And for some reason this is causing a crash.
- (NSString *) description {
NSMutableString *output = [NSMutableString string];
[output appendFormat:#"MyObject.description = %#\n", self.description];
return output;
}
Why would this be an issue?
Short Answer: The crash is a result of a stack overflow because your -description method calls itself repeatedly. To do what you want to do (accessing the ivar from within the description method), you should not use the prefix self. in front of the ivar.
More Detail:
In Objective-C, self.description is shorthand for [self description]. Using the dot-syntax informs the compiler that you want to access a property named description, and not the ivar itself.
It's an issue because you're creating an infinite loop. self.description will call [self description], which is exactly the method you're within. Hence you have the method calling itself repeatedly.
- (NSString *) description {
NSMutableString *output = [NSMutableString string];
[output appendFormat:#"super's description = %#\n", [super description]];
[output appendFormat:#"MyObject.description = %#\n", description];
return output;
}
You can access the instance variable directly, rather than using self.description. Also, I added an extra line to show how you can call super's description method (which doesn't create an infinite loop).

Getting argument values from NSInvocation

Could someone please explain how to go about getting the values passed to a non-existant method that is being intercepted when using:
+ (void)forwardInvocation:(NSInvocation *)anInvocation;
+ (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector;
Given a message like:
[SomeClass doSomething:#"theThing" withSomething:#"aParam"];
I can get the method signature without a problem but I am terribly confused about how to get the values that were passed in with it.
Am I totally off base in when I should use these methods or just missing something?
-[NSInvocation getArgument:atIndex:]
So in your case, you would use it like:
__unsafe_unretained NSString * firstArgument = nil;
__unsafe_unretained NSString * secondArgument = nil;
[theInvocation getArgument:&firstArgument atIndex:2];
[theInvocation getArgument:&secondArgument atIndex:3];
NSLog(#"Arguments: %# and %#", firstArgument, secondArgument);
Remember that self and _cmd are arguments 0 and 1.