tecnicas reflexion qué personal persona introspectivo introspectiva introspección introspeccionismo introspeccion ejemplos definicion objective-c cocoa xcode reflection introspection

objective c - reflexion - Objetivo C Introspección/Reflexión



reflexion introspectiva (6)

A falta del método de description (como .toString () en Java), no he oído hablar de uno que estuviera incorporado, pero no sería muy difícil crear uno. La Referencia de tiempo de ejecución de Objective-C tiene un conjunto de funciones que puede usar para obtener información sobre las variables de instancia de un objeto, métodos, propiedades, etc.

¿Existe un método incorporado, función, API, forma comúnmente aceptada, etc. para volcar el contenido de un objeto instanciado en Objective C, específicamente en el entorno Cocoa / Cocoa-Touch de Apple?

Quiero ser capaz de hacer algo como

MyType *the_thing = [[MyType alloc] init]; NSString *the_dump = [the_thing dump]; //pseudo code NSLog("Dumped Contents: %@", the_dump);

y mostrar los nombres y valores de la variable de instancia del objeto, junto con los métodos disponibles para llamar en tiempo de ejecución. Idealmente en un formato fácil de leer.

Para desarrolladores familiarizados con PHP, básicamente busco el equivalente de las funciones de reflexión ( var_dump() , get_class_methods() ) y la OO Reflection API.


Esto es lo que estoy usando actualmente para imprimir automáticamente las variables de la clase, en una biblioteca para su eventual publicación pública; funciona descargando todas las propiedades desde la clase de instancia hasta el árbol de la herencia. Gracias a KVC no es necesario que se preocupe si una propiedad es de tipo primitivo o no (para la mayoría de los tipos).

// Finds all properties of an object, and prints each one out as part of a string describing the class. + (NSString *) autoDescribe:(id)instance classType:(Class)classType { NSUInteger count; objc_property_t *propList = class_copyPropertyList(classType, &count); NSMutableString *propPrint = [NSMutableString string]; for ( int i = 0; i < count; i++ ) { objc_property_t property = propList[i]; const char *propName = property_getName(property); NSString *propNameString =[NSString stringWithCString:propName encoding:NSASCIIStringEncoding]; if(propName) { id value = [instance valueForKey:propNameString]; [propPrint appendString:[NSString stringWithFormat:@"%@=%@ ; ", propNameString, value]]; } } free(propList); // Now see if we need to map any superclasses as well. Class superClass = class_getSuperclass( classType ); if ( superClass != nil && ! [superClass isEqual:[NSObject class]] ) { NSString *superString = [self autoDescribe:instance classType:superClass]; [propPrint appendString:superString]; } return propPrint; } + (NSString *) autoDescribe:(id)instance { NSString *headerString = [NSString stringWithFormat:@"%@:%p:: ",[instance class], instance]; return [headerString stringByAppendingString:[self autoDescribe:instance classType:[instance class]]]; }


He hecho cocoapod de esto, https://github.com/neoneye/autodescribe

Modifiqué el código de Christopher Pickslay, lo convertí en una categoría en NSObject y también le agregué una prueba de unidad. Aquí es cómo usarlo:

@interface TestPerson : NSObject @property (nonatomic, strong) NSString *firstName; @property (nonatomic, strong) NSString *lastName; @property (nonatomic, strong) NSNumber *age; @end @implementation TestPerson // empty @end @implementation NSObject_AutoDescribeTests -(void)test0 { TestPerson *person = [TestPerson new]; person.firstName = @"John"; person.lastName = @"Doe"; person.age = [NSNumber numberWithFloat:33.33]; NSString *actual = [person autoDescribe]; NSString *expected = @"firstName=John/nlastName=Doe/nage=33.33"; STAssertEqualObjects(actual, expected, nil); } @end


Hice un par de ajustes al código de Kendall para imprimir los valores de las propiedades, lo cual fue muy útil para mí. Lo definí como un método de instancia en lugar de un método de clase, así es como lo llama la recursión de la clase superior. También agregué el manejo de excepciones para propiedades no compatibles con KVO, y agregué saltos de línea a la salida para facilitar la lectura (y la diferencia):

-(NSString *) autoDescribe:(id)instance classType:(Class)classType { NSUInteger count; objc_property_t *propList = class_copyPropertyList(classType, &count); NSMutableString *propPrint = [NSMutableString string]; for ( int i = 0; i < count; i++ ) { objc_property_t property = propList[i]; const char *propName = property_getName(property); NSString *propNameString =[NSString stringWithCString:propName encoding:NSASCIIStringEncoding]; if(propName) { @try { id value = [instance valueForKey:propNameString]; [propPrint appendString:[NSString stringWithFormat:@"%@=%@/n", propNameString, value]]; } @catch (NSException *exception) { [propPrint appendString:[NSString stringWithFormat:@"Can''t get value for property %@ through KVO/n", propNameString]]; } } } free(propList); // Now see if we need to map any superclasses as well. Class superClass = class_getSuperclass( classType ); if ( superClass != nil && ! [superClass isEqual:[NSObject class]] ) { NSString *superString = [self autoDescribe:instance classType:superClass]; [propPrint appendString:superString]; } return propPrint; }


Honestamente, la herramienta correcta para este trabajo es el depurador de Xcode. Tiene toda esta información fácilmente accesible de una manera visual. Tómese el tiempo para aprender a usarlo, es una herramienta realmente poderosa. Más información: Xcode Debugging Guide .


ACTUALIZACIÓN: Cualquiera que esté buscando hacer este tipo de cosas podría querer ver el envoltorio ObjC de Mike Ash para el tiempo de ejecución de Objective-C .

Esto es más o menos cómo lo harías:

#import <objc/runtime.h> . . . -(void)dumpInfo { Class clazz = [self class]; u_int count; Ivar* ivars = class_copyIvarList(clazz, &count); NSMutableArray* ivarArray = [NSMutableArray arrayWithCapacity:count]; for (int i = 0; i < count ; i++) { const char* ivarName = ivar_getName(ivars[i]); [ivarArray addObject:[NSString stringWithCString:ivarName encoding:NSUTF8StringEncoding]]; } free(ivars); objc_property_t* properties = class_copyPropertyList(clazz, &count); NSMutableArray* propertyArray = [NSMutableArray arrayWithCapacity:count]; for (int i = 0; i < count ; i++) { const char* propertyName = property_getName(properties[i]); [propertyArray addObject:[NSString stringWithCString:propertyName encoding:NSUTF8StringEncoding]]; } free(properties); Method* methods = class_copyMethodList(clazz, &count); NSMutableArray* methodArray = [NSMutableArray arrayWithCapacity:count]; for (int i = 0; i < count ; i++) { SEL selector = method_getName(methods[i]); const char* methodName = sel_getName(selector); [methodArray addObject:[NSString stringWithCString:methodName encoding:NSUTF8StringEncoding]]; } free(methods); NSDictionary* classDump = [NSDictionary dictionaryWithObjectsAndKeys: ivarArray, @"ivars", propertyArray, @"properties", methodArray, @"methods", nil]; NSLog(@"%@", classDump); }

A partir de ahí, es fácil obtener los valores reales de las propiedades de una instancia, pero debe verificar si son tipos u objetos primitivos, por lo que era demasiado flojo para ponerlo. También puede optar por escanear la cadena de herencia para obtener todas las propiedades definidas en un objeto. Luego hay métodos definidos en categorías, y más ... Pero casi todo está disponible.

Aquí hay un extracto de lo que el código anterior volca para UILabel:

{ ivars = ( "_size", "_text", "_color", "_highlightedColor", "_shadowColor", "_font", "_shadowOffset", "_minFontSize", "_actualFontSize", "_numberOfLines", "_lastLineBaseline", "_lineSpacing", "_textLabelFlags" ); methods = ( rawSize, "setRawSize:", "drawContentsInRect:", "textRectForBounds:", "textSizeForWidth:", . . . ); properties = ( text, font, textColor, shadowColor, shadowOffset, textAlignment, lineBreakMode, highlightedTextColor, highlighted, enabled, numberOfLines, adjustsFontSizeToFitWidth, minimumFontSize, baselineAdjustment, "_lastLineBaseline", lineSpacing, userInteractionEnabled ); }