Rick's Random Ramblings

Month

June 2010

1 post

Unrecognized Selector

This post is a little more technical than I’ve done, but I figured I’d share this in hope that it can help other ObjC developers.

Objective-C is a pretty dynamically typed language.  I’m sure there’s a great Computer Science term for exactly the type of typing ObjC fits into, but I probably slept through that class, cause honestly… who the fuck cares.  What matters is that the compiler will happily let you do this:

NSNumber *number = [NSNumber numberWithInt: 8];
NSString *string = (NSString *)number;


From now on, you think string is a string, but in reality it’s a number.  Naturally you wouldn’t actually do that in code (right??), but if you’re reading in a file off the disk that wasn’t created by your app but another (like say… another app’s Info.plist), and reading a certain value that according to the specs say should be a string like CFBundleVersion, and assume you’re always dealing with a string, you’re probably going to end up in a bad situation.

You might see something like this console logs that users send in along with their “Feature Blah doesn’t work!” email.

6/14/10 6:40:19 PM    Bodega[1509]    -[NSCFNumber length]: unrecognized selector sent to instance 0x123a5f000

OK, so somewhere, I have a NSNumber that’s being passed around when it shouldn’t be.  I don’t know where.  I don’t know what it’s supposed to be.  Through experience, I know that it’s likely that I’m expecting a NSString cause length isn’t a popular method name, but that doesn’t really help narrow it down.  

Like any good bug, step 1 is to get it to reproduce locally.  I have no great solution to that one.  But once you’ve got your unrecognized selector being called locally, figuring out where this is actually happening tends to be a pain in the ass.  

Luckily, Objective-C’s categories can come in and save the day.  We’d like to know where we’re treating a NSNumber like a NSString: where we’re calling [number length].  That method doesn’t exist, hence the log statement.  So why don’t we just add the method and see?  It’s as easy as throwing these few lines into any of your .m files or even into a new one.

@implementation NSNumber (LengthSelector)

- (NSInteger)length
{
   return 42;
}

@end

That creates a category on NSNumber that allows us to add methods to it.  Let’s add the length method.  That won’t solve our problem, but it’ll allow us to set a breakpoint on that return 42 line.  Build & Debug, and the debugger will stop at that line.  Follow the stack trace up and you’ll see where you’re calling length.  You can then start seeing where you get that variable from, and figure out where you’re assuming it’s of one type when obviously it’s not.  

When you’re done, just take the code out, since you probably don’t want the useless method returning a bogus value to stick around.

Jun 20, 2010
Next page →
2011 2012
  • January
  • February
  • March 1
  • April 1
  • May
  • June
  • July
  • August 1
  • September 1
  • October
  • November
  • December
2010 2011 2012
  • January 3
  • February 2
  • March 1
  • April
  • May 1
  • June 2
  • July 1
  • August 1
  • September
  • October
  • November
  • December 1
2009 2010 2011
  • January 7
  • February 3
  • March 2
  • April 5
  • May 1
  • June 1
  • July 1
  • August
  • September 2
  • October 1
  • November 1
  • December
2009 2010
  • January
  • February
  • March
  • April
  • May
  • June
  • July
  • August
  • September 4
  • October 3
  • November
  • December 1