NSObject is the base / root class for nearly every hierarchy of classes in Objective-C applications. NSObject provides the basic functionality that you take for granted when using Objective-C, such as providing the ability to retain and release objects.
From first time iPhone developers to Objective-C gurus, it’s hard to deny the usefulness of NSLog() and ‘gdb> po’ when trying to figure out just which part of your application is causing problems at a given time. NSLog works wonders for outputting strings combined with integers, floats etc. For example, consider the following code and corresponding output.
NSLog(@"Integration timestep: %d, x: %f, y: %f", timestep, x_value, y_value);> 2011-01-19 10:20:30.123 AppName[…] Integration timestep: 10, x: 5.4, y:2.3
However, the default implementation doesn’t really know how to print anything useful for your custom objects. For instance, if you attempt the following, you don’t really get useful information.
NSLog(@"%@", self);> 2011-01-19 10:20:30.123 AppName[…] <ClassName: 0x000000>
The output that you see is simply the class name and the memory address of the object that you’re logging. This is the default functionality provided by NSObject’s description, which is what is called when a subclass of NSObject is ‘converted to a string’ (which is done by calling the description method).
So, in order to output something more useful, it’s a good idea to override the NSObject description method. For example, you might want to override the description method for your base game object class to do something like the following;
-(NSString *)description { return [NSString stringWithFormat:@"<GameObject: %@, Position: %f, %f>", [self objectID], [self position].x, [self position].y]; }
Now, if you call NSLog on a game object, you will be presented with a much nicer string of the form
<GameObject: playerOne, Position: 200, 300>.
This is even more useful, when you consider that your entities will more than likely be in an array, or dictionary of some form. In this scenario, printing your list of game objects would go from…
> po [self gameObjects] { <GameObject:0x100000>, <GameObject:0x101000>, <GameObject:0x102000>, }
to…
> po [self gameObjects] { <GameObject: playerOne, Position: 100, 100> <GameObject: playerTwo, Position: 600, 300> <GameObject: ball, Position: 200, 300> }
While it’s easy to skip past this stage when building custom objects, it’s definitely worth the extra 2 minutes per class that it takes to write the description method. Especially when you consider the amount of time it’ll likely save you down the road when you’re debugging.
Including Class Name and Address
If you would like to extend the default design and include the class name and address in the object’s description, then you can do so using the following codes in your NSString format string with the relevant parameter afterwards.
"%@": NSStringFromClass([self class]) "%p": self
%@ simply represents a placeholder for another string, and we can get the class name for an object as a string by using NSStringFromClass([self class]). %p will be replaced by the address of an object, and so we simply need to pass the corresponding reference to self to include the address of an object this way.
So, for a state in a state machine you might want to output the following information;
-(NSString *)description { return [NSString stringWithFormat:@"<%@: %p, ID: %d>", NSStringFromClass([self class]), self, [self stateId]]; }
Which would output debug information of the form; <ClassName: 0xfa707a0, ID: 1001>