2009-05-06 12 views
19

tengo la sensación de que esta es la pregunta estúpida, pero voy a preguntar de todos modos ...crear instancias de clase personalizada de NSDictionary

que tienen una colección de objetos cuya NSDictionary pares clave/valor corresponden a una clase personalizada I' creado, llámalo MyClass. ¿Existe un método fácil o de "mejor práctica" para que básicamente haga algo como MyClass * instance = [ map propiedades a MyClass];? Tengo la sensación de que necesito hacer algo con NSCoding o NSKeyedUnarchiver, pero en lugar de tropezar solo, me imagino que alguien podría señalarme en la dirección correcta.

Respuesta

26

The -setValu esForKeysWithDictionary: método, junto con -dictionaryWithValuesForKeys :, es lo que desea utilizar.

Ejemplo:

// In your custom class 
+ (id)customClassWithProperties:(NSDictionary *)properties { 
    return [[[self alloc] initWithProperties:properties] autorelease]; 
} 

- (id)initWithProperties:(NSDictionary *)properties { 
    if (self = [self init]) { 
     [self setValuesForKeysWithDictionary:properties]; 
    } 
    return self; 
} 

// ...and to easily derive the dictionary 
NSDictionary *properties = [anObject dictionaryWithValuesForKeys:[anObject allKeys]]; 
+2

Eso es bastante útil, y setValuesForPropertiesWithKeys es el camino a seguir. Hace exactamente lo que hace mi código, ¡y está integrado! Buen hallazgo –

+0

Es un método maravilloso. Utilizando eso en conjunto con la API objc_ *, puede construir una clase de serialización automática (para que pueda dejar de escribir esos engorrosos métodos -initWithCoder: y -encodeWithCoder: – retainCount

+0

Awesome. Eso va a ser útil. –

3

Suponiendo que la clase cumpla con el protocolo Key-Value Coding, podría utilizar lo siguiente: (definida como una categoría en NSDictionary por conveniencia):

// myNSDictionaryCategory.h: 
@interface NSDictionary (myCategory) 
- (void)mapPropertiesToObject:(id)instance 
@end 


// myNSDictionaryCategory.m: 
- (void)mapPropertiesToObject:(id)instance 
{ 
    for (NSString * propertyKey in [self allKeys]) 
    { 
     [instance setValue:[self objectForKey:propertyKey] 
        forKey:propertyKey]; 
    } 
} 

Y aquí es cómo lo usaría:

#import "myNSDictionaryCategory.h" 
//... 
[someDictionary mapPropertiesToObject:someObject]; 
+0

Gracias. Esto era lo que estaba buscando. :) – LucasTizma

+0

¡De nada! –

+2

Parece que Red tiene una respuesta aún mejor. Te recomiendo que cambies la respuesta aceptada a su :) –

6

No hay allKeys en NSObject. Tendrá que crear una categoría adicional en NSObject, como a continuación:

NSObject + PropertyArray.h

@interface NSObject (PropertyArray) 
- (NSArray *) allKeys; 
@end 

NSObject + PropertyArray.m

#import <objc/runtime.h> 

@implementation NSObject (PropertyArray) 
- (NSArray *) allKeys { 
    Class clazz = [self class]; 
    u_int count; 

    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); 

    return [NSArray arrayWithArray:propertyArray]; 
} 
@end 

Ejemplo:

#import "NSObject+PropertyArray.h" 

... 

MyObject *obj = [[MyObject alloc] init]; 
obj.a = @"Hello A"; //setting some values to attributes 
obj.b = @"Hello B"; 

//dictionaryWithValuesForKeys requires keys in NSArray. You can now 
//construct such NSArray using `allKeys` from NSObject(PropertyArray) category 
NSDictionary *objDict = [obj dictionaryWithValuesForKeys:[obj allKeys]]; 

//Resurrect MyObject from NSDictionary using setValuesForKeysWithDictionary 
MyObject *objResur = [[MyObject alloc] init]; 
[objResur setValuesForKeysWithDictionary:objDict]; 
+0

, podría agregar otro método en esta categoría: - (NSDictionary *) dictionaryWithValuesForKeys { return [self dictionaryWithValuesForKeys: [self allKeys]]; } – jpalten

0

Si estás haciendo este tipo de cosas, lo más probable es que estés lidiando con JSON y probablemente deberías echarle un vistazo a Mantle https://github.com/Mantle/Mantle

A continuación, recibirá un método conveniente dictionaryValue

[anObject dictionaryValue]; 
0

Sólo tiene que añadir la categoría de NSObject para conseguir dictionaryRepresentation de los objetos personalizados (en mi caso el uso de serialización JSON solamente):

// NSObject+JSONSerialize.h 
#import <Foundation/Foundation.h> 

@interface NSObject(JSONSerialize) 

- (NSDictionary *)dictionaryRepresentation; 

@end 

// NSObject+JSONSerialize.m 
#import "NSObject+JSONSerialize.h" 
#import <objc/runtime.h> 

@implementation NSObject(JSONSerialize) 

+ (instancetype)instanceWithDictionary:(NSDictionary *)aDictionary { 
    return [[self alloc] initWithDictionary:aDictionary]; 
} 

- (instancetype)initWithDictionary:(NSDictionary *)aDictionary { 
    aDictionary = [aDictionary clean]; 

    self.isReady = NO; 

    for (NSString* propName in [self allPropertyNames]) { 
     [self setValue:aDictionary[propName] forKey:propName]; 
    } 

    //You can add there some custom properties with wrong names like "id" 
    //[self setValue:aDictionary[@"id"] forKeyPath:@"objectID"]; 
    self.isReady = YES; 

    return self; 
} 

- (NSDictionary *)dictionaryRepresentation { 
    NSMutableDictionary *result = [NSMutableDictionary dictionary]; 
    NSArray *propertyNames = [self allPropertyNames]; 

    id object; 
    for (NSString *key in propertyNames) { 
     object = [self valueForKey:key]; 
     if (object) { 
      [result setObject:object forKey:key]; 
     } 
    } 

    return result; 
} 

- (NSArray *)allPropertyNames { 
    unsigned count; 
    objc_property_t *properties = class_copyPropertyList([self class], &count); 

    NSMutableArray *rv = [NSMutableArray array]; 

    unsigned i; 
    for (i = 0; i < count; i++) { 
     objc_property_t property = properties[i]; 
     NSString *name = [NSString stringWithUTF8String:property_getName(property)]; 
     [rv addObject:name]; 
    } 
    //You can add there some custom properties with wrong names like "id" 
    //[rv addObject:@"objectID"]; 
    //Example use inside initWithDictionary: 
    //[self setValue:aDictionary[@"id"] forKeyPath:@"objectID"]; 

    free(properties); 

    return rv; 
} 

@end 

Además, puede ver que mi solución no funcionará con objetos personalizados con matrices o objetos anidados. Para matrices: simplemente cambie las líneas de código en el método dictionaryRepresentation:

if (object) { 
     if ([object isKindOfClass:[NSArray class]]) { 
      @autoreleasepool { 
       NSMutableArray *array = [NSMutableArray array]; 
       for (id item in (NSArray *)object) { 
        [array addObject:[item dictionaryRepresentation]]; 
       } 

       [result setObject:array forKey:key]; 
      } 
     } else { 
      [result setObject:object forKey:key]; 
     } 
    } 
Cuestiones relacionadas