views:

134

answers:

4

I am pretty sure I am just missing the point here and getting confused. Can anyone tell me how I might write a simple description for an object that will print out the value of its instance variables to the console.

Also: is there anyway to present the information as a block (i.e. if you had 10 iVars its going to be a pain getting them all to return one by one)

@interface CelestialBody : NSObject {
    NSString *bodyName;
    int bodyMass;
}

- (NSString *)description { 
    return (@"Name: %@ Mass: %d", bodyName, bodyMass);
}

cheers -gary-

+9  A: 
- (NSString*)description
{
  return [NSString stringWithFormat:@"Name: %@\nMass: %d\nFoo: %@",
     bodyName, bodyMass, foo];
}
Jason Coco
Thanks guys, I was forgetting that I needed to format the string using NSString. Much appreciated ...
fuzzygoat
@quinn: please don't arbitrarily edit my posts. If I have a spelling mistake or something and you're desperate to fix it, feel free, but don't arbitrarily reformat my code snippets into your favorite style. It doesn't add any value.
Jason Coco
+2  A: 

Look at the answer to this question. The code is reproduced below:

unsigned int varCount;

Ivar *vars = class_copyIvarList([MyClass class], &varCount);

for (int i = 0; i < varCount; i++) {
    Ivar var = vars[i];

    const char* name = ivar_getName(var);
    const char* typeEncoding = ivar_getTypeEncoding(var);

    // do what you wish with the name and type here
}

free(vars);
Martin Gordon
This is useful in a generic sense, but Jason's answer addresses the problem the asker had with using a format string correctly.
Quinn Taylor
If you have the name of the ivar then using `valueForKey:` to get the value of the ivar through KVC removes the burden to check the type for you, everything is then an object.
PeyloW
A: 

As Jason wrote you should use stringWithFormat: to format strings with printf like syntax.

-(NSString*)description;
{
  return [NSString stringWithFormat:@"Name: %@ Mass: %d", bodyName, bodyMass];
}

To avoid writing this over and over again for many classes you could add a category on NSObject that allows you to inspect instance variables easily. This will be bad performance, but works for debugging purposes.

@implementation NSObject (IvarDictionary)

-(NSDictionary*)dictionaryWithIvars;
{
  NSMutableDictionary* dict = [NSMutableDictionary dictionary];
  unsigned int ivarCount;
  Ivar* ivars = class_copyIvarList([self class], &ivarCount);
  for (int i = 0; i < ivarCount; i++) {
    NSString* name = [NSString stringWithCString:ivar_getName(ivars[i])
                                        encoding:NSASCIIStringEncoding];
    id value = [self valueForKey:name];
    if (value == nil) {
      value = [NSNull null];
    }
    [dict setObject:value forKey:name];
  }
  free(vars);
  return [[dict copy] autorelease]; 
}
@end

With this in place implementing description is also a piece of cake:

-(NSString*)description;
{
  return [[self dictionaryWithIvars] description];
}

Do not add this description as a category on NSObject, or you might end up with infinite recursions.

PeyloW
+1  A: 

That's not a bad idea what you had there, it's almost achievable too.

// choose a short name for the macro
#define _f(x,...) [NSString stringWithFormat:x,__VA_ARGS__]

...

- (NSString *) description
{
    return _f(@"Name: %@ Mass: %d", bodyName, bodyMass);
}
dreamlax
Thanks, I always wondered how I could shorten that overly verbose stringWithFormat idiom, but I didn't know how to do var args in macros. Now I do :)
Virgil Dupras