2009-05-06 6 views
19

У меня есть ощущение, что это глупый вопрос, но я спрошу так или иначе ...инстанцирование пользовательского класса от NSDictionary

У меня есть коллекция NSDictionary объектов, пар ключ/значение соответствует пользовательскому классу I» вы создали, назовите его MyClass. Есть ли простой или «лучший метод» для меня, чтобы в основном сделать что-то вроде MyClass * instance = [ map NSDictionary Недвижимость до MyClass];? У меня есть чувство, что мне нужно что-то сделать с NSCoding или NSKeyedUnarchiver, но вместо этого спотыкаться через него самостоятельно, я считаю, что кто-то мог бы указать мне в правильном направлении.

ответ

26

The -setValu esForKeysWithDictionary: метод, наряду с -dictionaryWithValuesForKeys :, то, что вы хотите использовать.

Пример:

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

Это очень удобно, и setValuesForPropertiesWithKeys - это путь. Он делает именно то, что делает мой код, и он встроен! Хорошая находка. –

+0

Это замечательный метод. Используя это в сочетании с API objc_ *, вы можете построить класс автосериализации (так что вы можете прекратить писать эти громоздкие методы -initWithCoder: and -encodeWithCoder:) – retainCount

+0

Awesome. Это пригодится. –

3

Предполагая, что ваш класс соответствует протоколу Key-Value Coding, можно использовать следующее: (определяется как категория по NSDictionary для удобства):

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

А вот как ты будет использовать его:

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

Спасибо. Это то, что я искал. :) – LucasTizma

+0

Добро пожаловать! –

+2

Похоже, у Красного есть еще лучший ответ. Я рекомендую вам переключить принятый ответ на его :) –

6

Там нет allKeys на NSObject. Вам нужно создать дополнительную категорию на NSObject, как показано ниже:

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 

Пример:

#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

вы также можете добавить еще один метод в эту категорию: - (NSDictionary *) dictionaryWithValuesForKeys { return [self dictionaryWithValuesForKeys: [self allKeys]]; } – jpalten

0

Если вы делаете такие вещи, вы имеете дело с JSON, и вам, вероятно, стоит взглянуть на Mantle https://github.com/Mantle/Mantle

Вы тогда получите удобный метод dictionaryValue

[anObject dictionaryValue]; 
0

Просто добавьте категорию NSObject для получения dictionaryRepresentation из пользовательских объектов (в моем случае, используя только JSON сериализации):

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

Также вы можете видеть, что мое решение не будет работать с пользовательскими объектами с вложенными объектами или массивами. Для массивов - просто измените строки кода в 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]; 
     } 
    }