Creating Custom Object Descriptions For Debugging

As you may be aware, every object in Objective-C has a description method that returns a string. This is similar to the toString method in languages like Java and C#.

The purpose of the description method is not really just to convert objects to strings; NSNumber has a stringValue method for that purpose, NSArray has componentsJoinedByString: and NSDate can be “stringified” using an NSDateFormatter, etc. The description method’s main purpose is for debugging.

You typically use the description method in one of two ways. Either in code, using NSLog, as follows:

NSLog(@"My object value %@", someObject);

Or in the console, like this (the “po” command stands for “print object”):

> po someObject

Note that it’s not necessary to call the description method explicitly here; you can if you want, but attempting to print an object in the console, or use it in a format string, automatically calls the description method.

The description method for NSObject produces something like this:

<NSObject: 0x1094462e0>

This is the basic description implementation that is inherited by all objects, unless it’s overriden. It displays the object class and the memory address as a hex value. The class name tells us what kind of object it is. The hex value is useful because it can be used in the console to reference an object if we don’t have an explicit variable name to reference it with (e.g. if it’s inside a collection object like NSArray or NSDictionary). For example, to call a method on our object and see the result we could say:

> po [0x1094462e0 someMethod]

Custom Descriptions

So the basic description is handy, but it doesn’t tell us anything about the object’s sub-properties. Many of the standard Cocoa framework objects override description to show other data. NSDate returns the date in ISO format. NSNumber returns a string representation of its value. NSString returns its text (probably a good idea, since description returns a string, and you wouldn’t want the result of every description method to be <NSString: 0x1094462e0>).

It’s actually a bit inconvenient that these classes don’t display their class or address in the description, but fortunately you can work around that. The following console commands will print the class and memory address of an array, instead of just its children:

> po [someString class]
__NSCFString
> p someString
(id) $6 = 0x000000010951b5e0

The description method for a Class just returns its name, so we can “po” the class of an object to get the class name (in this case it’s __NSCFString not NSString, because NSString is a class cluster). The “p” command is short for “print”. This is normally used for printing primitive types like NSInteger or double, but it works on objects too; it just prints the hex value of their memory address instead of calling the description method. It also assigns them to a temporary variable (in this case $6) so that they can be referenced using that variable subsequently:

> po $6

“Value types” like NSDate, NSString and NSNumber are fairly unusual in the way that they implement the description method. most classes that have a customized description methods return something like this (for NSArray):

<__NSArrayM 0x10900d2f0>(
SomeString,
5,
<NSObject: 0x1094462e0>
)

Or this (for UIWindow):

<UIWindow: 0x109008fa0; frame = (0 0; 320 568); hidden = YES; 
  gestureRecognizers = <NSArray: 0x10900d2f0>; layer = <UIWindowLayer: 0x109008f40>>

Essentially, this is the basic NSObject description, but with some additional sub-properties of the class appended. This is much more useful for debugging, because we can see those values at a glance instead of having to “po” them all individually.

So how can we implement a custom description like this for our own classes? Suppose we have a class called Foo with a few properties:

@interface Foo : NSObject 
 
@property (nonatomic, assign) NSInteger property1;
@property (nonatomic, assign) BOOL property2;
@property (nonatomic, copy) NSString *property3;
 
@end

To write the description, we first need to print the class name and object address, then add in the new data. The code for that would look like this:

@implementation Foo 
 
- (NSString *)description
{
  return [NSString stringWithFormat:@"<%@: %p; property1 = %zd; 
   property2 = %@; property3 = %@>", [self class], self,
   property1, property2? @"YES": @"NO", property3];
}
 
@end

(Note the correct % format strings for each type. %p gives us hex output for our object’s memory address, and %zd will print an NSInteger on either 32 or 64 bit architectures without warnings).

OK, so that gives us some nice output. It’s a pain to rewrite that method for every class though, and to update it each time we add or modify a property. Isn’t there a better way?

Introspection (Again!)

Last week in NSCoding, Without the Boilerplate we talked about using introspection to automatically get a list of the property names for our classes. We won’t repeat that explanation here, but suffice to say that if you read that article, you’ll know how to write a magic propertyNames method that returns the names of all the properties of your class.

The requirements for printing properties are slightly different than for NSCoding. We don’t want to omit properties that don’t have backing ivars for example, because we might be trying to describe something like a Core Data NSManagedObject that has purely virtual properties. We also don’t really need to cache the property names this time because we won’t be calling our description method often enough to have a performance impact.

We’ll call the new method “describablePropertyNames” so it doesn’t clash with the codable properties method (in case we want to use both automatic NSCoding and debug description technique in the same project). Here’s the code:

// Import the Objective-C runtime headers
#import <objc/runtime.h> 
 
- (NSArray *)describablePropertyNames
{
  // Loop through our superclasses until we hit NSObject
  NSMutableArray *array = [NSMutableArray array];
  Class subclass = [self class];
  while (subclass != [NSObject class])
  {
    unsigned int propertyCount;
    objc_property_t *properties = class_copyPropertyList(subclass,&propertyCount);
    for (int i = 0; i < propertyCount; i++)
    {
      // Add property name to array
      objc_property_t property = properties[i];
      const char *propertyName = property_getName(property);
      [array addObject:@(propertyName)];
    }
    free(properties);
    subclass = [subclass superclass];
  }
 
  // Return array of property names
  return array;
}

Given such a method, we can now rewrite our custom description as follows:

@implementation DescribableObject 
 
- (NSString *)description
{
  NSMutableString *propertyDescriptions = @"";
  for (NSString *key in [self describablePropertyNames])
  {
    id value = [self valueForKey:key];
    [propertyDescriptions appendFormat:@"; %@ = %@", key, value];
  }
  return [NSString stringWithFormat:@"<%@: 0x%x%@>", [self class], 
   (NSUInteger)self, propertyDescriptions];
}
 
@end

If we put this method in a base class that is inherited by all of our models, we have an automatic way to get detailed descriptions of all our objects.

A slight problem with this method is that it’s a bit too descriptive. By recursively describing every property of the object, and all their properties, and so on, we may end up lost in a sea of data if we try to print the description of the root object in a large tree. Also, it’s kind of a shame that this method only works on objects that inherit from our base class, because we might like to use it with Core Data objects, or UIViews, etc.

We can solve both problems by changing the method name to “longDescription”, and making it a category on NSObject. This way, we can call it on any object, and it will only call the regular description method for its child objects, so their descriptions won’t be expanded unless we choose to dig deeper. That would look like this:

 
#ifdef DEBUG
 
@interface NSObject (LongDescription)
 
- (NSString *)longDescription;
 
@end
 
@implementation NSObject (LongDescription) 
 
- (NSArray *)describableProperties
{
  ...
}
 
- (NSString *)longDescription
{
  ...
}
 
@end
 
#endif

Note the “#ifdef DEBUG” – Since the category is only used for debugging, it makes sense to exclude it from release builds. If you don’t want to do that, you might want to consider prefixing it with a namespace (XYZ_longDescription), just in case Apple decides to add their own longDescription method in future.

Remember that only the standard description method gets called automatically, so to call our longDescription method you must invoke it explicitly, e.g.

> po [someObject longDescription]

You can easily add other variants such as shortDescription (which only returns a subset of properties) or recursiveDescription (that calls recursiveDescription on all its child objects, for the rare occasions where you do want to see the whole object graph at once). The possibilities are endless.


Nick Lockwood is the author of iOS Core Animation: Advanced Techniques. Nick also wrote iCarousel, iRate and other Mac and iOS open source projects.