2009-05-06 16 views
19

ho la sensazione che questo è stupido domanda, ma io chiederò comunque ...instantiating personalizzato classe da NSDictionary

Ho una collezione di oggetti la cui NSDictionary coppie chiave/valore corrispondono a una classe personalizzata I' creato, chiamalo MyClass. C'è un metodo facile o "best practice" per fare fondamentalmente qualcosa come MyClass * instance = [ mappa NSDictionary proprietà a MyClass];? Ho la sensazione che ho bisogno di fare qualcosa con NSCoding o NSKeyedUnarchiver, ma piuttosto che inciampare attraverso di esso da solo, immagino che qualcuno là fuori potrebbe essere in grado di indicarmi la giusta direzione.

risposta

26

The -setValu esForKeysWithDictionary: method, insieme a -dictionaryWithValuesForKeys :, è ciò che si desidera utilizzare.

Esempio:

// 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

Questo è piuttosto comodo, e setValuesForPropertiesWithKeys è la strada da percorrere. Fa esattamente quello che fa il mio codice, ed è integrato! Bella scoperta. –

+0

È un metodo meraviglioso. Usandolo in combinazione con l'API objc_ *, puoi creare una classe di serializzazione automatica (così puoi smettere di scrivere quegli ingombranti -initWithCoder: e -encodeWithCoder: metodi) – retainCount

+0

Impressionante. Sarà utile. –

3

Supponendo che la classe sia conforme al protocollo Key-Value Coding, è possibile utilizzare il seguente: (definito come una categoria a NSDictionary per comodità):

// 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]; 
    } 
} 

Ed ecco come si lo userei:

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

Grazie. Questo era quello che stavo cercando. :) – LucasTizma

+0

Prego! –

+2

Sembra che Red abbia una risposta ancora migliore. Vi consiglio di passare la risposta accettata al suo :) –

6

Non c'è allKeys su NSObject. È necessario creare una categoria in più sul NSObject come di seguito:

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 

Esempio:

#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

potresti anche aggiungere un altro metodo in questa categoria: - dizionario (NSDictionary *) ConValuesForKeys { return [self dictionaryWithValuesForKeys: [self allKeys]]; } – jpalten

0

Se stai facendo questo genere di cose è probabile che hai a che fare con JSON e probabilmente dovresti dare un'occhiata a Mantle https://github.com/Mantle/Mantle

Sarà quindi ottenere un metodo conveniente dictionaryValue

[anObject dictionaryValue]; 
0

Basta aggiungere categoria per NSObject per ottenere dictionaryRepresentation dai vostri oggetti personalizzati (nel mio caso utilizzando in JSON solo serializzazione):

// 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 

Inoltre, è possibile vedere che la mia soluzione non funzionerà con oggetti personalizzati con oggetti o matrici annidate. Per gli array, è sufficiente modificare le righe di codice nel metodo 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]; 
     } 
    }