2010-08-16 15 views
14

Necesito determinar el tipo de propiedad de un objeto (pasado por nombre) para realizar la deserialización desde XML. Tengo algunos pseudo-código general (sin embargo estoy seguro de cómo realizar estas comparaciones en Objective-C):En Objective-C Determine si una propiedad es un int, float, double, NSString, NSDate, NSNumber, etc.

id object = [[[Record alloc] init] autorelease]; 

NSString *element = @"date"; 
NSString *data = @"2010-10-16"; 

objc_property_t property = class_getProperty([object class], [element UTF8String]); 
const char *attributes = property_getAttributes(property); 

char buffer[strlen(attributes) + 1]; 
strcpy(buffer, attributes); 

char *attribute = strtok(buffer, ","); 
if (*attribute == 'T') attribute++; else attribute = NULL; 

if (attribute == NULL); 
else if (strcmp(attribute, "@\"NSDate\"") == 0) [object setValue:[NSDate convertToDate:self.value] forKey:element]; 
else if (strcmp(attribute, "@\"NSString\"") == 0) [object setValue:[NSString convertToString:self.value] forKey:element]; 
else if (strcmp(attribute, "@\"NSNumber\"") == 0) [object setValue:[NSNumber convertToNumber:self.value] forKey:element]; 

he mirado a través de la class_getProperty y property_getAttributes, sin embargo todavía no estoy seguro de cómo hacer la comparaciones anteriores.

Respuesta

36

@ La respuesta de Ahruman es correcta, si se trata de objetos. Permítanme sugerir algunas alternativas:

  1. valueForKey:: si utiliza [myObject valueForKey:@"myPropertyName"], devolverá un objeto. Si la propiedad corresponde a algún tipo de primitiva (int, float, CGRect, etc.), se enmarcará en un NSNumber o NSValue (según corresponda). Si vuelve como NSNumber, puede extraer fácilmente una representación doble (doubleValue) y usar eso como NSTimeInterval para crear un NSDate. Probablemente recomendaría este enfoque.
  2. Caso especial de cada tipo. property_getAttributes() devuelve un char* que representa todos los atributos de la propiedad, y se puede extraer del tipo al hacer esto:

    const char * type = property_getAttributes(class_getProperty([self class], "myPropertyName")); 
    NSString * typeString = [NSString stringWithUTF8String:type]; 
    NSArray * attributes = [typeString componentsSeparatedByString:@","]; 
    NSString * typeAttribute = [attributes objectAtIndex:0]; 
    NSString * propertyType = [typeAttribute substringFromIndex:1]; 
    const char * rawPropertyType = [propertyType UTF8String]; 
    
    if (strcmp(rawPropertyType, @encode(float)) == 0) { 
        //it's a float 
    } else if (strcmp(rawPropertyType, @encode(int)) == 0) { 
        //it's an int 
    } else if (strcmp(rawPropertyType, @encode(id)) == 0) { 
        //it's some sort of object 
    } else ....

    Ésta es pedante más correcta que la respuesta de Luis, porque mientras más tipos tienen un solo carácter codificación, no tienen que. (Su sugerencia supone una codificación de un solo carácter)

  3. Por último, si usted está haciendo esto en una subclase de NSManagedObject, a continuación, me gustaría animar a retirar NSPropertyDescription.

De estas alternativas, probablemente pueda ver que dejar el cuadro de ejecución el valor para usted es probablemente el más simple.

edición extracción del tipo:

en el código anterior, se puede extraer el nombre de la clase, así:

if ([typeAttribute hasPrefix:@"[email protected]"] && [typeAttribute length] > 1) { 
    NSString * typeClassName = [typeAttribute substringWithRange:NSMakeRange(3, [typeAttribute length]-4)]; //turns @"NSDate" into NSDate 
    Class typeClass = NSClassFromString(typeClassName); 
    if (typeClass != nil) { 
    [object setValue:[self convertValue:self.value toType:typeClass] forKey:element]; 
    } 
} 

Y a continuación, en lugar de utilizar las categorías de métodos de clase para hacer la conversión (es decir, , [NSDate convertToDate:]), realice un método en self que lo haga por usted y acepte el tipo deseado como parámetro. Se podría (por ahora) Hazlo como:

- (id) convertValue:(id)value toType:(Class)type { 
    if (type == [NSDate class]) { 
    return [NSDate convertToDate:value]; 
    } else if (type == [NSString class]) { 
    return [NSString convertToString:value]; 
    } ... 
} 

Una parte de mí se pregunta, sin embargo: ¿por qué estás necesidad de hacer las cosas de esta manera? ¿Qué estás haciendo?

+0

+1 valueForKey:/setValueForKey :. El autoboxing/unboxing usa NSNumber para tipos básicos y NSValue para tipos compuestos (es decir, structs); para obtener más detalles, consulte http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/KeyValueCoding/Concepts/DataTypes.html –

+0

¡Gracias Dave por la respuesta detallada! No estoy seguro de cómo utilizar el primer método (¿no va a valueForKey devolver nil hasta que haya terminado de decodificar el objeto, ya que todas las propiedades serán nulas? El segundo método (el método que he estado intentando), funciona para escalares, pero no parece funcionar para non-escalars. Por ejemplo, '@encode (NSDate *)' da "@", pero obtener los atributos para '@property (no atómico, retener) NSDate * date' da "T @" NSDate ", &, N, Vdate" (no estoy seguro de por qué la sección de atributo no es la misma, dice que usa @encode). ¿Alguna idea? Actualicé mi pregunta con mi último código. ¡Gracias! –

+1

@Kevin sí, el La primera opción requiere una instancia del objeto. En cuanto a la segunda, se me olvidó que la propiedad codifica un poco más acerca del tipo que '@ encode'. Sin embargo, podría usar esta como ventaja. Si el primer carácter es' @ ', luego vea si puede extraer un nombre de clase entre las marcas '' ' –

0

[object isKindOfClass:[NSDate class]], etc.

+2

Esto no funcionará porque no tengo acceso al objeto de propiedad (será nulo antes de que el objeto se deserialice). –

3

Si sólo se preocupan por las clases que se pueden utilizar isKindOfClass:, pero si usted quiere tratar con escalares Tiene razón en que es necesario utilizar property_getAttributes(), devuelve una cadena que codifica la información de tipo . A continuación se muestra una función básica que demuestra lo que debe hacer. Para ver ejemplos de cadenas de codificación, mira here.

unsigned int outCount, i; 
objc_property_t *properties = class_copyPropertyList([object class], &outCount); 
for (i = 0; i < outCount; i++) { 
    objc_property_t property = properties[i]; 
    char *property_name = property_getName(property); 
    char *property_type = property_getAttributes(property); 

    switch(property_type[1]) { 
     case 'f' : //float 
     break; 
     case 's' : //short 
     break; 
     case '@' : //ObjC object 
     //Handle different clases in here 
     break; 
    } 
} 

Obvviously tendrá que añadir todos los tipos y clases que necesita para manejar a esto, se utiliza el ObjC normales @encode types.

+0

Tenga en cuenta que los atributos de propiedad no son solo codificaciones de tipo Objective-C, sino que también codifican información sobre la propiedad en sí (por ejemplo, los nombres de los getters y setters no predeterminados que representa, su atomicidad y comportamiento de administración de memoria, etc.). –

+0

Es cierto, aunque en el ejemplo anterior, estoy desviando el segundo carácter de la cadena, que es el tipo de información. El resto de la información requiere un código de análisis más complejo. –