1

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-

fuzzygoat
  • 26,573
  • 48
  • 165
  • 294

4 Answers4

9
- (NSString*)description
{
  return [NSString stringWithFormat:@"Name: %@\nMass: %d\nFoo: %@",
     bodyName, bodyMass, foo];
}
Jason Coco
  • 77,985
  • 20
  • 184
  • 180
  • Thanks guys, I was forgetting that I needed to format the string using NSString. Much appreciated ... – fuzzygoat Sep 17 '09 at 21:36
  • 1
    @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 Sep 18 '09 at 20:29
5

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);
Community
  • 1
  • 1
Martin Gordon
  • 36,329
  • 7
  • 58
  • 54
  • 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 Sep 17 '09 at 20:08
  • 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 Sep 17 '09 at 21:08
1

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
  • 36,742
  • 12
  • 80
  • 99
1

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
  • 93,976
  • 29
  • 161
  • 209
  • 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 Oct 08 '10 at 09:11
  • As a side note, you might consider using `##__VA_ARGS__` for more generic variadic argument treatment within macros; otherwise you'll probably get a compiler error for something like `return _f(@"Name is foo");`, where you're providing no variadic argument list in the invocation. – fullofsquirrels Apr 13 '16 at 20:23
  • @fullofsquirrels: but why would you use `_f(@"str")` when you should just use `@"str"`? – dreamlax Apr 13 '16 at 23:46
  • @dreamlax, in this single case, you're right, you probably wouldn't. I'm thinking of the case of a more general purpose logging system/API, where you just want to throw a random set of arguments at a single method and have it handle things appropriately, rather than having to create a different macro just to handle that special case. – fullofsquirrels Apr 14 '16 at 15:20