Learn Objective-C on the Mac - Mark Dalrymple [123]
So, to make this work, edit the invalidArgumentException_unrecognizedSelector method, changing the line inside the for loop to the following:
Because we know that NSObject, and therefore every object, implements the description method and returns a string, we know that this call will now always work. Even if someone slips another kind of object in there that doesn’t give us a nice, compact result like NSString and NSNumber do, at least we know the method is there and will be called!
So do the fix, Build & Run, and see what shows up in the Xcode output console:
Dang, another one! Quit the app, and run it again, once again time using the Run➤Debug menu item, which enables the breakpoint to halt the program when an exception is raised. When the program halts, you can once again enter some commands at the gdb prompt to get some info:
And if you scan down the call stack looking for the first location that shows black instead of grey, you’ll find -[ExceptionCityAppDelegate invalidArgumentException_insertNil], the location of the piece of our code that triggered the exception. Click that line, and you’ll see the code editor window highlight this line in invalidArgumentException_insertNil:
Take a look at the code just above that, and you’ll see the problem: object2 is a pointer to nil, and NSMutableArray won’t allow you to insert a nil into it. In a complex application, you may want to track down the root cause (where did the nil pointer come from, and is nil a valid value for that variable?), but in this case we’ll work around the problem by putting a safety check before adding the object, like this:
That takes care of that. NSInvalidArgumentException is one of the most frequently encountered exceptions in Cocoa, and you’ve just seen the two most frequent situations that trigger it: calling a method on a class which doesn’t implement that method, and trying to insert nil into an array.
Build & Run the app again, and get ready for the next problem.
NSRangeException
The previous exceptions have been cleared up, but look what happens now:
Crikey! That’s one big index value. Stop the app, rerun in the debugger with Run➤Debug, wait for the halt, and see what we’ve got:
Now look at the call stack, and click on the uppermost item that’s within our code: - [ExceptionCityAppDelegate rangeException]. This will highlight the following line in the text editor:
This line actually makes two calls: first to the objectAtIndex: method, then to the NSLog function. A glance at the call stack shows that it’s the objectAtIndex: method that is complaining. Apparently it doesn’t like the value contained in indexOfFive. If you look in the variable view, or type p indexOfFive (note the use of p for standard C types, as opposed to po for Objective-C objects), you’ll see 2147483647. That does seem a bit high! If you look at the code where it’s set, just two lines earlier, you’ll see this:
That line is asking array for the index of an object that it doesn’t actually contain. In this case, NSArray returns a special integer value called NSNotFound, which is defined to be the maximum possible integer value. On Mac OS X running in 32-bit mode, that turns out to be 2147483647. This value is used to tell the caller that, “Hey, that object you want the index of? I don’t have it.” Which can be pretty useful to know! A consequence of this is that whenever you get a value from indexOfObject:, you really have to check to make