How to catch unrecognized selector sent to instance exception? - objective-c

I am getting after some time unrecognized selector sent to instance exception. When i get this i want just skip it and my app should work.
However i don't know how to catch. As this don't catch:
#property(nonatomic,retain) UIButton *button;
#try{
if(button.currentBackgroundImage == nil){//rises exception
}
}#catch(NSException *e){
}
How i could handle this ?
Thanks.

The technique I use and see often is: instead of catching the exception, check if the object responds to the selector:
if(![button respondsToSelector:#selector(currentBackgroundImage)] || button.currentBackgroundImage == nil) {
// do your thing here...
}

If you are getting this exception, it means there is a design flaw, a bug in your code. Patching it by ignoring the exception is not the right thing to do. Try to pin down why you are sending the wrong message to the wrong object instead. Your code will become more robust and maintainable.
Also, sometimes you get this exception when the object originally was of the right type, but is halfway in the process of being deallocated. Watch out!
If you still want to bypass the exception, read Apple's docs where it explains the multi-step process by which messages are bound to method implementations at run time. There is at least two places where you can catch it by overriding NSObject's default behavior.

I understand the answers telling you to prevent the unrecognized selector since that is the preferred method.
But in the case where you do not have that option (such as in my case, Cocoa internals messing up further down the call stack) you can indeed catch the unrecognized selector as you tried.
Proof of concept code:
// Do a really bad cast from NSObject to NSButton
// to get something to demonstrate on
NSButton *object = (NSButton*)[[NSObject alloc] init];
#try{
// Log the description as the method exists
// on both NSObject and NSButton
NSLog(#"%#", [object description]);
// Send an unrecognized selector to NSObject
[object bounds];
} #catch(NSException *e){
NSLog(#"Catch");
} #finally {
NSLog(#"Finally");
}
// Print the description to prove continued execution
NSLog(#"Description again: %#", [object description]);
Output:
2019-02-26 14:11:04.246050+0100 app[46152:172456] <NSObject: 0x60000000a6f0>
2019-02-26 14:11:04.246130+0100 app[46152:172456] -[NSObject bounds]: unrecognized selector sent to instance 0x60000000a6f0
2019-02-26 14:11:04.246226+0100 app[46152:172456] Catch
2019-02-26 14:11:04.246242+0100 app[46152:172456] Finally
2019-02-26 14:11:04.246258+0100 app[46152:172456] Description again: <NSObject: 0x60000000a6f0>
As you can see the exception is still logged to the console, but the code execution continues.

Related

textFieldDidBeginEditing using tags

I'm running into a little issue with my textFieldDidBeginEditing method..
I'm trying to figure out which textfield is being called on to edit so I can decide if I want the view to move up or not to make the field visible.
Here is my method, I have commented some things out to try to find out where the error is:
- (void)textFieldDidBeginEditing:(UITextField *)sender
{
NSLog(#"This method is called");
//[self.view setFrame:CGRectMake(0,-120,320,568)];
if(sender.tag == _nameF.tag)
{
NSLog(#"This if is called");
//[self.view setFrame:CGRectMake(0,-120,320,568)];
}
else
{
NSLog(#"Else called instead");
}
}
I see "This method is called" in the log, so I know the method is being called in the first place, but after that, I see this:
2013-07-23 12:27:18.654 SidebarDemo[2110:60b] -[NSConcreteNotification tag]: unrecognized selector sent to instance 0x15d7b8c0
2013-07-23 12:27:18.655 SidebarDemo[2110:60b] * Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSConcreteNotification tag]: unrecognized selector sent to instance 0x15d7b8c0'
This leads me to believe that it is something with sender.tag, but I don't see anything wrong with my code, to my knowledge.
What could the issue be here? Is there another method I can use to find out what textfield is being edited?
Thanks.
Since you are setting up the UITextFieldTextDidBeginEditingNotification notification to call your textFieldDidBeginEditing: method, you need to change the method parameter. And to avoid confusion with the corresponding UITextFieldDelegate method, you should rename this method as well (which means you need to update the line of code that register the notification handler).
- (void)textFieldDidBeginEditingHandler:(NSNotification *)notification {
UITextField *textField = (UITextField *)notification.object;
// It's OK to use == here since we really do want to compare pointer values
if(textField == _nameF) {
NSLog(#"This if is called");
//[self.view setFrame:CGRectMake(0,-120,320,568)];
} else {
NSLog(#"Else called instead");
}
}
There is no need for tags since you have ivars for each text field.
BTW - why are you using notifications for this? Why not use the UITextFieldDelegate methods?

-[NSNull objectForKeyedSubscript:]: unrecognized selector sent to instance

I got an exception that says:
-[NSNull objectForKeyedSubscript:]: unrecognized selector sent to instance
Is it saying I am trying to access an NSNull object with a key?
Any idea what causes this and how to fix it or debug further?
The way to fix it is to not attempt objectForKeyedSubscript on an NSNull object. (I'm betting you're handling some JSON data and aren't prepared for a NULL value.)
(And apparently objectForKeyedSubscript is what the new array[x] notation translates into.)
(Note that you can test for NSNull by simply comparing with == to [NSNull null], since there's one and only one NSNull object in the app.)
What ever value you are storing, despite what the editor tells you, at run time you are storing an NSNull, and later on trying to call objectForKeyedSubscript. I am guessing this happening on what is expected to be an NSDictionary. Some thing like:
NSString *str = dict[#"SomeKey"]
Either a piece of code beforehand is not doing its job and investigate there, or perform some validation:
NSDictionary *dict = ...;
if ( [dict isKindOfClass:[NSDictionary class]] ) {
// handle the dictionary
}
else {
// some kind of error, handle appropriately
}
I often have this kind of scenario when dealing with error messages from networking operations.
I suggest adding a category to NSNull to handle this in the same way you would expect a subscript call to be handled if it it were sent to nil.
#implementation NSNull (Additions)
- (NSObject*)objectForKeyedSubscript:(id<NSCopying>)key {
return nil;
}
- (NSObject*)objectAtIndexedSubscript:(NSUInteger)idx {
return nil;
}
#end
A simple way to test is like this:
id n = [NSNull null];
n[#""];
n[0];
With this category, this test should be handled successfully/softly.

Why is it that sending any selector to a Nil object does nothing, but sending an "invalid" selector to any NSObject raises an exception?

Does anyone know why NextStep/Apple decided to take the "convenient method" of doing nothing when passing a Nil object a message, but the "Java method" of raising an exception when passing an instantiated object an invalid selector?
For example,
// This does "nothing"
NSObject *object = Nil;
[object thisDoesNothing];
object = [[NSObject alloc] init];
// This causes an NSInvalidArgumentException to be raised
[object thisThrowsAnException];
So on one hand, we have the convenience of not having to check for Nil (assuming we don't care too much about the result of the method call)--but on the other hand we have to check for an exception if our object doesn't respond to a method?
If I'm not sure if the object will respond, I either have to:
#try {
[object thisThrowsAnException];
} #catch (NSException *e){
// do something different with object, since we can't call thisThrowsAnException
}
Or,
if([object respondsToSelector:#selector(thisThrowsAnException)]) {
[object thisThrowsAnException];
}
else {
// do something different with object, since we can't call thisThrowsAnException
}
(The latter is probably the better way to do it, since if object is Nil, the selector would NOT raise an exception, thus your code might not behave the way you want it to).
My question is:
WHY did Apple decide to implement it this way?
Why not have the unrecognized selector call to an instantiated object not raise an exception?
Alternatively, why not have the Nil object raise an exception if you try to call a method on it?
I can't fully answer your question, but I can answer part of it. Objective-C allows you to send a message to nil because it makes code more elegant. You can read about this design decision here, and I will steal its example:
Let's say you want to get the last phone number that some person dialed on her office phone. If you can't send messages to nil, you have to write it like this:
Office *office = [somePerson office];
// Person might not have an office, so check it...
if (office) {
Telephone *phone = [office telephone];
// The office might not have a telephone, so check it...
if (phone) {
NSString *lastNumberDialed = [phone lastNumberDialed];
// The phone might be brand new, so there might be no last-dialed-number...
if (lastNumberDialed) {
// Use the number, for example...
[myTextField setText:lastNumberDialed];
}
}
}
Now suppose you can send messages to nil (and always get nil back):
NSString *lastNumberDialed = [[[somePerson office] telephone] lastNumberDialed];
if (lastNumberDialed) {
[myTextField setText:lastNumberDialed];
}
As for why sending an unrecognized selector to an object raises an exception: I don't know for sure. I suspect that it's far more common for this to be a bug than to be harmless. In my code, I only want an unrecognized selector to be silently ignored when I need to send an optional protocol message (e.g. sending an optional message to a delegate). So I want the system to treat it as an error, and let me be explicit in the relatively rare case when I don't want it to be an error.
Note that you can tinker (to some extent) with the handling of unrecognized selectors in your own classes, in a few different ways. Take a look at the forwardingTargetForSelector:, forwardInvocation:, doesNotRecognizeSelector:, and resolveInstanceMethod: methods of NSObject.
From the good ol' documentation:
In Objective-C, it is valid to send a message to nil—it simply has no
effect at runtime.
As for the other problem of the unrecognized selector behavior, an old implementation file of NSObject (from the MySTEP library) shows that the culprit is the NSObject method -doesNotRecognizeSelector:, which looks a bit as follows:
- (void) doesNotRecognizeSelector:(SEL)aSelector
{
[NSException raise:NSInvalidArgumentException
format:#"NSObject %#[%# %#]: selector not recognized",
object_is_instance(self)?#"-":#"+",
NSStringFromClass([self class]),
NSStringFromSelector(aSelector)];
}
Which means that ObjC methods could feasibly be tinkered with so that they do not in fact have to raise an error. Which means the decision was entirely arbitrary, just like the decision to switch to "method-eating" messages to nil. A feat which can be done through method swizzling NSObject (wholly dangerous, as it will raise an EXC_BAD_ACCESS, or EXC_I386_BPT on mac, but at least it doesn't raise an exception)
void Swizzle(Class c, SEL orig, SEL new)
{
Method origMethod = class_getInstanceMethod(c, orig);
Method newMethod = class_getInstanceMethod(c, new);
if(class_addMethod(c, orig, method_getImplementation(newMethod), method_getTypeEncoding(newMethod)))
class_replaceMethod(c, new, method_getImplementation(origMethod), method_getTypeEncoding(origMethod));
else
method_exchangeImplementations(origMethod, newMethod);
}
-(void)example:(id)sender {
Swizzle([NSObject class], #selector(doesNotRecognizeSelector:), #selector(description));
[self performSelector:#selector(unrecog)];
}
The category:
#implementation NSObject (NoExceptionMessaging)
-(void)doesNotRecognizeSelector:(SEL)aSelector {
NSLog(#"I've got them good ol' no exception blues.");
}
#end
For everyone's amusement, due to the discussion CodaFi and I were having, here's a quickly-hacked-together way to eat normally unresponded-to messages and have them return nil:
#interface EaterOfBadMessages : NSObject
#end
#implementation EaterOfBadMessages
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
NSMethodSignature * sig = [super methodSignatureForSelector:aSelector];
if( !sig ){
sig = [NSMethodSignature signatureWithObjCTypes:"##:"];
}
return sig;
}
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
id nilPtr = nil;
[anInvocation setReturnValue:&nilPtr];
}
#end
int main(int argc, const char * argv[])
{
#autoreleasepool {
EaterOfBadMessages * e = [[EaterOfBadMessages alloc] init];
// Of course, pre-ARC you could write [e chewOnThis]
NSLog(#"-[EaterOfBadMessages chewOnThis]: %#", [e performSelector:#selector(chewOnThis)]);
}
return 0;
}
Please don't use this in real life.

Unrecognized selector sent to instance - why?

OK so I have a code with an class object called "game". Every frame (60 FPS) I update that object with function that gets a string. After like 5 seconds of running the game I'm getting the unrecognized selector sent to instance error.
The update:
[game updatePlayersAndMonsters:#"0" monsters:#"0"];
The function:
-(void)updatePlayersAndMonsters:(NSString*)players monsters:(NSString*)monsters {
CCLOG(#"%#.%#", players, monsters);
}
I don't understand what's going on.
The error:
2011-07-03 12:13:19.175 app[65708:207] -[NSCFString updatePlayersAndMonsters:monsters:]: unrecognized selector sent to instance 0xc4e95b0
2011-07-03 12:13:19.176 app[65708:207] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSCFString updatePlayersAndMonsters:monsters:]: unrecognized selector sent to instance 0xc4e95b0'
What should I do? Thanks. Also IDK if any other details you need, so just write if I forget something, I just don't have an idea.
UPDATE:
Gmae is object of class GameNode:
+(id) GmameNodeWithMapID:(int)MapID_ scene:(SomeScene*)MainScene_ players:(NSString*)Cplayers_ monsters:(NSString*)Cmonsters_ monsterCount:(NSString*)monsterCount_
{
return [[[self alloc] GmameNodeWithMapID:MapID_ scene:MainScene_ players:Cplayers_ monsters:Cmonsters_ monsterCount:monsterCount_] autorelease];
}
-(id) GmameNodeWithMapID:(int)MapID scene:(SomeScene*)MainScene players:(NSString*)Cplayers monsters:(NSString*)Cmonsters monsterCount:(NSString*)monsterCount
{
if( (self=[super init])) {
I create it with:
game = [GameNode GmameNodeWithMapID:ChoosenMapID scene:self players:Thing[5] monsters:Thing[6] monsterCount:Thing[4]];
UPDATE 2
I create the SomeScene:
+(id) scene {
CCScene *s = [CCScene node];
id node = [SomeScene node];
[s addChild:node];
return s;
}
-(id) init {
if( (self=[super init])) {
I use it:
[[CCDirector sharedDirector] replaceScene: [CCTransitionRadialCW transitionWithDuration:1.0f scene:[LoginScene scene]]];
Since you imply that the update function [game updatePlayersAndMonsters:#"0" monsters:#"0"]; is called for the first 5 seconds of your game and then you get the error, my guess is that the game object is not correctly retained, so it gets deallocated and the successive attempt of sending a message to it fails because some NSString object has been reusing its memory (and it does not have a updatePlayersAndMonsters:monsters selector).
Please share how game is created (alloc/init) and how it is stored in your classes to help you further.
Activating NSZombies tracking could also help to diagnose this.
EDIT: after you adding the code
It seems to me that in the line:
game = [GameNode GmameNodeWithMapID:ChoosenMapID scene:self players:Thing[5] monsters:Thing[6] monsterCount:Thing[4]];
you are setting either a local variable or an ivar to your autoreleased GameNode.
Now, since you are not using a property, nor I can see any retain on your autoreleased GameNode, my hypothesis seems confirmed. Either assign to a retain property:
self.game = [GameNode ...];
being game declared as:
#property (nonatomic, retain)...
or do a retain yourself:
game = [[GameNode GmameNodeWithMapID:ChoosenMapID scene:self players:Thing[5] monsters:Thing[6] monsterCount:Thing[4]] retain];
'NSInvalidArgumentException', reason: '-[NSCFString updatePlayersAndMonsters:monsters:]: means tht you are trying to send updatePlayersAndMonsters to a String object. Are you reassigning game to point to something else?

NSInvalidArgumentException... how do I define the arguments correctly?

I'm getting this exception on the following code. I think it's because I have not defined the two incoming parameter types. They are local; so how do I define them (and where).
Error: 2011-04-27 11:18:03.226
PointPeek[174:707] * Terminating app
due to uncaught exception
'NSInvalidArgumentException', reason:
'+[SQLiteDB addRecordToDatabase::]:
unrecognized selector sent to class
0x1fe70'
Here's the calling line of code:
[SQLiteDB addRecordToDatabase:
symbol.data: symbol.typeName];
and here's the method I'm calling:
- (void) addRecordToDatabase:data: typeName {
NSString *insertCommand = [NSString stringWithFormat:#"INSERT INTO CardData (CARD_ID, CARD_NAME, CODE_VAL) VALUES ('/%#', '/%#', '/%#')", data, #"Test Card", typeName];
if(sqlite3_open_v2(cDatabasePath, &db, SQLITE_OPEN_READWRITE, NULL) == SQLITE_OK) {
}
Error: 2011-04-27 11:18:03.226
PointPeek[174:707] * Terminating app
due to uncaught exception
'NSInvalidArgumentException', reason:
'+[SQLiteDB addRecordToDatabase::]:
unrecognized selector sent to class
0x1fe70'
Basically, the "unrecognized selector sent to..." message means you tried to tell an object (or class) to do something it doesn't know how to do. ("selector" is another name for method).
You defined your method of the SQLiteDB class as an instance method:
- (void) addRecordToDatabase:data: typeName;
We know that because of the - in the method name (see Methods and Messaging and Class Interface). In the error message you got, notice that it began with a +, which means you attempted to call a method on the SQLiteDB class itself, rather than on an instance of that class.
In other words, you attempted to do this:
[SQLiteDB addRecordToDatabase: symbol.data: symbol.typeName];
when you needed to do something like this:
SQLiteDB *db = [[[SQLiteDB alloc] init] autorelease]; // an instance
[db addRecordToDatabase: symbol.data: symbol.typeName];
(Note that the previous 2 lines of code aren't all that useful in and of themselves. Presumably, instead of creating an instance of SQLiteDB in this method, you'd have it as an instance variable).
[SQLiteDB addRecordToDatabase: symbol.data: symbol.typeName];
That'd assume that addRecordToDabase:: is a class method, not an instance method.
Furthermore, that is an awful name for a method. Try something like:
- (void)addRecordWithData:(NSData*)aData andType:(NSString*)aType;
That is, bare :s are to be avoided and you should always specify the type of the parameter (and not fall back to id as you did here).
Finally, why aren't you using Core Data or, at the very least, FMDB? Raw SQLite is a waste of time.
SQLite is harder to write code for than Core Data, most likely. If you are a newbie to both, Core Data is a better return on investment of your time.
In any case, the questions in your comment indicate that you really need to start by understanding Objective-C. Apple provides an excellent language guide.