Создание пользовательского класса из NSDictionary - PullRequest
19 голосов
/ 06 мая 2009

У меня такое чувство, что это глупый вопрос, но я все равно спрошу ...

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

Ответы [ 5 ]

26 голосов
/ 06 мая 2009

-setValuesForKeysWithDictionary: метод вместе с -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]];
6 голосов
/ 19 октября 2011

На NSObject нет allKeys. Вам нужно будет создать дополнительную категорию для 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];
3 голосов
/ 06 мая 2009

Предполагая, что ваш класс соответствует протоколу 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 голосов
/ 30 января 2017

Просто добавьте категорию для 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];
        }
    }
0 голосов
/ 02 декабря 2014

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

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

[anObject dictionaryValue];
...