2011-06-16 12 views
9

En Objective-C, es común anular -description con un método que imprime el ID del objeto y los nombres/valores de las variables de instancia. Me gustaría hacer un método genérico -description que lo haga a través de la introspección, en lugar de hacerlo manualmente para cada clase. Me gustaría que la salida sea algo como:Método genérico de descripción Objective-C para imprimir valores ivar

<ClassName: 0x??????, ivar1: value1, ivar2: value2, ivar3: value3, ...> 

También sería bueno para ordenar por ejemplo, nombre de la variable (por lo que están siempre en el mismo orden). Finalmente, si esto se puede poner en una categoría que anule con seguridad la funcionalidad NSObject, sería perfecto (pero veo que no es sencillo, ya que NSObject.m tiene una advertencia sobre el uso de -[NSString stringWithFormat:] en -description).

Respuesta

12

¡Esta es una gran pregunta! Como no he encontrado nada como esto, escribí una pequeña función que hace esto por ti. Reemplaza - (NSString *)description de NSObject utilizando el método swizzling (sí, soy este mal. MAHAHAHAHAHahahahaha) e imprime el ivar en el orden en que también aparecen en la clase (puede editarlo fácilmente para mostrarlos en orden alfabético).

DO NOT! olvida llamar al NSObjectSwizzleDescription()!

archivo .h:

@interface NSObject (JSObjectAdditions) 
@end 


void NSObjectSwizzleDescription(); 

archivo .m:

#import <objc/objc.h> 
#import "JSObject.h" 

@implementation NSObject (JSObjectAdditions) 

- (NSString *)verboseDescription 
{ 
    NSMutableString *description = [NSMutableString stringWithFormat:@"<%@: %p>", NSStringFromClass([self class]), self]; 

    uint32_t ivarCount; 
    Ivar *ivars = class_copyIvarList([self class], &ivarCount); 

    if(ivars) 
    { 
     [description appendString:@"\n{"]; 

     for(uint32_t i=0; i<ivarCount; i++) 
     { 
      Ivar ivar = ivars[i]; 
      const char *ivarType = ivar_getTypeEncoding(ivar); 
      id ivarObject = object_getIvar(self, ivar); 

      [description appendFormat:@"\n %s: ", ivar_getName(ivar)]; 

      // Default signed data types 
      if(strcmp(ivarType, "c") == 0) 
      { 
       char character = (char)ivarObject; 
       [description appendFormat:@"'%c'", character]; 
      } 
      else if(strcmp(ivarType, "i") == 0 || strcmp(ivarType, "l") == 0) // l is also 32 bit in the 64 bit runtime environment 
      { 
       int integer = (int)ivarObject; 
       [description appendFormat:@"%i", integer]; 
      } 
      else if(strcmp(ivarType, "s") == 0) 
      { 
       short shortVal = (short)ivarObject; 
       [description appendFormat:@"%i", (int)shortVal]; 
      } 
      else if(strcmp(ivarType, "q") == 0) 
      { 
       long long longVal = (long long)ivarObject; 
       [description appendFormat:@"%l", longVal]; 
      } 
      // Default unsigned data types 
      else if(strcmp(ivarType, "C") == 0) 
      { 
       unsigned char chracter = (unsigned char)ivarObject; 
       [description appendFormat:@"'%c'", chracter]; 
      } 
      else if(strcmp(ivarType, "I") == 0 || strcmp(ivarType, "L") == 0) 
      { 
       unsigned int integer = (unsigned int)ivarObject; 
       [description appendFormat:@"%u", integer]; 
      } 
      else if(strcmp(ivarType, "S") == 0) 
      { 
       unsigned short shortVal = (unsigned short)ivarObject; 
       [description appendFormat:@"%i", (int)shortVal]; 
      } 
      else if(strcmp(ivarType, "Q") == 0) 
      { 
       unsigned long long longVal = (unsigned long long)ivarObject; 
       [description appendFormat:@"%ll", longVal]; 
      } 
      // Floats'n'stuff 
      else if(strcmp(ivarType, "f") == 0) 
      { 
       float floatVal; 
       memcpy(&floatVal, &ivarObject, sizeof(float)); 

       [description appendFormat:@"%f", floatVal]; 
      } 
      else if(strcmp(ivarType, "d") == 0) 
      { 
       double doubleVal; 
       memcpy(&doubleVal, &ivarObject, sizeof(double)); 

       [description appendFormat:@"%f", doubleVal]; 
      } 
      // Boolean and pointer 
      else if(strcmp(ivarType, "B") == 0) 
      { 
       BOOL booleanVal = (BOOL)ivarObject; 
       [description appendFormat:@"%@", (booleanVal ? @"YES" : @"NO")]; 
      } 
      else if(strcmp(ivarType, "v") == 0) 
      { 
       void *pointer = (void *)ivarObject; 
       [description appendFormat:@"%p", pointer]; 
      } 
      else if(strcmp(ivarType, "*") == 0 || strcmp(ivarType, ":") == 0) // SEL is just a typecast for a cstring 
      { 
       char *cstring = (char *)ivarObject; 
       [description appendFormat:@"\"%s\"", cstring]; 
      } 
      else if(strncmp(ivarType, "@", 1) == 0) 
      { 
       [description appendFormat:@"%@", ivarObject]; 
      } 
      else if(strcmp(ivarType, "#") == 0) 
      { 
       Class objcClass = (Class)ivarObject; 
       [description appendFormat:@"%s", class_getName(objcClass)]; 
      } 
      else 
       [description appendString:@"???"]; 
     } 

     [description appendString:@"\n}"]; 
     free(ivars); 
    } 

    return description; 
} 

@end 


void NSObjectSwizzleDescription() 
{ 
    Method origMethod = class_getInstanceMethod([NSObject class], @selector(description)); 
    Method newMethod = class_getInstanceMethod([NSObject class], @selector(verboseDescription)); 

    method_exchangeImplementations(origMethod, newMethod); 
} 
+0

+1 para la palabra "Swizzling" – jrdioko

+0

1 y temible. Agregar todas las cadenas a un 'NSMutableArray' y devolver' [array componentsJoinedByString: @ ""] 'mejoraría un poco el rendimiento al guardar las asignaciones, aunque NSMutableString's' appendFormat' es * not * 'O (n^2)' por lo que no es crítico. – orip

1

No conozco ningún código que haga esto, pero si eso existe, ¡me interesarían las depuraciones!

Me sorprende que no haya encontrado algo como esto, classic techniques simplemente funciona, pero sería bueno tener algún tipo de método inspect, que es, creo, posible utilizando la reflexión, pero tal vez ' Estoy mal.

Sugeriría echar un vistazo a this interesting post, que está relacionado con lo que está buscando, y podría ser una base para implementarlo.

Otra cosa interesante es también NSLogger, que es una buena herramienta para la depuración.

Cuestiones relacionadas