2012-05-21 12 views
6

Estoy tratando de obtener una lista de todas las propiedades de una clase desconocida y la clase de cada propiedad. En el momento en que obtengo una lista de todas las propiedades de un objeto (lo hago de forma recursiva para obtener todas las superclases). Me inspiré en this postiOS obtener clase de propiedad

+ (NSArray *)classPropsFor:(Class)klass 
{  
    NSLog(@"Properties for class:%@", klass); 
    if (klass == NULL || klass == [NSObject class]) { 
     return nil; 
    } 

    NSMutableArray *results = [[NSMutableArray alloc] init]; 

    unsigned int outCount, i; 
    objc_property_t *properties = class_copyPropertyList(klass, &outCount); 
    for (i = 0; i < outCount; i++) { 
     objc_property_t property = properties[i]; 
     const char *propName = property_getName(property); 
     if(propName) { 
      NSString *propertyName = [NSString stringWithUTF8String:propName]; 
      [results addObject:propertyName]; 
     } 
     NSArray* dict = [self classPropsFor:[klass superclass]]; 
     [results addObjectsFromArray:dict]; 
    } 
    free(properties); 

    return [NSArray arrayWithArray:results]; 
} 

Así que ahora quiero la clase de cada propiedad y lo hago:

NSArray* properties = [PropertyUtil classPropsFor:[self class]]; 
for (NSString* property in properties) { 
    id value= [self valueForKey:property]; 
    NSLog(@"Value class for key: %@ is %@", property, [value class]); 
} 

El problema es que funciona para NSStrings o pero no para clases personalizadas, para que me vuelve nulo. Quiero crear de forma recursiva un diccionario que represente un objeto que pueda tener otros objetos dentro y, como creo que necesito saber la clase de cada propiedad, ¿es posible?

Respuesta

2

Probablemente debería almacenar la clase (como una cadena) para cada propiedad al mismo tiempo que almacena el propertyName. Tal vez como un diccionario con el nombre de la propiedad como la clave y el nombre de la clase como el valor, o viceversa.

Para obtener el nombre de la clase, se puede hacer algo como esto (poner esto justo después se declara propertyName):

NSString* propertyAttributes = [NSString stringWithUTF8String:property_getAttributes(property)]; 
NSArray* splitPropertyAttributes = [propertyAttributes componentsSeparatedByString:@"\""]; 
if ([splitPropertyAttributes count] >= 2) 
{ 
    NSLog(@"Class of property: %@", [splitPropertyAttributes objectAtIndex:1]); 
} 

El código de gestión de cadena es porque los atributos incluyen una serie de piezas de información - la los detalles exactos se especifican aquí: https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html

+0

¡Hombre que es increíble y funciona perfecto! Gracias por su ayuda :) – Jpellat

+0

De nada :) –

2

ACTUALIZADO

Esto no funciona para valores que son nil. En su lugar, debe usar la API C de tiempo de ejecución para obtener la clase del método ivar o de acceso correspondiente.

+0

Si la propiedad es 'nil', ¿cómo generará esto la clase? – jlehr

+0

¡Gracias! Bueno, para los valores nulos :) – Jpellat

+0

De nada. Otra consideración es que invocar métodos de acceso puede tener efectos secundarios, por ejemplo, inicialización lenta, que quizás no desee desencadenar en este punto. – jlehr

7

Acabo de hacer un pequeño método para esto.

// Simple as. 
Class propertyClass = [customObject classOfPropertyNamed:propertyName]; 

Podría ser optimizado de muchas maneras, pero me encanta.


Implementación dice así:

-(Class)classOfPropertyNamed:(NSString*) propertyName 
{ 
    // Get Class of property to be populated. 
    Class propertyClass = nil; 
    objc_property_t property = class_getProperty([self class], [propertyName UTF8String]); 
    NSString *propertyAttributes = [NSString stringWithCString:property_getAttributes(property) encoding:NSUTF8StringEncoding]; 
    NSArray *splitPropertyAttributes = [propertyAttributes componentsSeparatedByString:@","]; 
    if (splitPropertyAttributes.count > 0) 
    { 
     // xcdoc://ios//library/prerelease/ios/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html 
     NSString *encodeType = splitPropertyAttributes[0]; 
     NSArray *splitEncodeType = [encodeType componentsSeparatedByString:@"\""]; 
     NSString *className = splitEncodeType[1]; 
     propertyClass = NSClassFromString(className); 
    } 
    return propertyClass; 
} 

Es parte de eppz!kit, dentro de un representador objeto desarrollar llama NSObject+EPPZRepresentable.h. Realmente hace lo que eres para lograr originalmente.

// Works vica-versa. 
NSDictionary *representation = [customObject dictionaryRepresentation]; 
CustomClass = [CustomClass representableWithDictionaryRepresentation:representation]; 

Codifica muchos tipos, colecciones mínimas ITERATE representa tipos CoreGraphics, UIColors, también representan/reconstruir referencias a objetos.


Nueva versión que escupe incluso nombres de tipo C y nombrados tipos struct así:

NSLog(@"%@", [self typeOfPropertyNamed:@"index"]); // unsigned int 
NSLog(@"%@", [self typeOfPropertyNamed:@"area"]); // CGRect 
NSLog(@"%@", [self typeOfPropertyNamed:@"keyColor"]); // UIColor 

Parte de eppz!model, no dude en utilizar implementaciones de métodos en https://github.com/eppz/eppz.model/blob/master/eppz!model/NSObject%2BEPPZModel_inspecting.m#L111

+3

¡Funciona como un encanto! – Kjuly

0

Lo siguiente es agregar lo siguiente a una categoría de objetos NSO.

- (Class) classForKeyPath:(NSString*)keyPath { 
    Class class = 0; 

    unsigned int n = 0; 
    objc_property_t* properties = class_copyPropertyList(self.class, &n); 
    for (unsigned int i=0; i<n; i++) { 
     objc_property_t* property = properties + i; 
     NSString* name = [NSString stringWithCString:property_getName(*property) encoding:NSUTF8StringEncoding]; 
     if (![keyPath isEqualToString:name]) continue; 

     const char* attributes = property_getAttributes(*property); 
     if (attributes[1] == '@') { 
      NSMutableString* className = [NSMutableString new]; 
      for (int j=3; attributes[j] && attributes[j]!='"'; j++) 
       [className appendFormat:@"%c", attributes[j]]; 
      class = NSClassFromString(className); 
     } 
     break; 
    } 
    free(properties); 

    return class; 
} 
Cuestiones relacionadas