Instantiating Custom Class from NSDictionary

匿名 (未验证) 提交于 2019-12-03 02:44:02

问题:

I have a feeling that this is stupid question, but I'll ask anyway...

I have a collection of NSDictionary objects whose key/value pairs correspond to a custom class I've created, call it MyClass. Is there an easy or "best practice" method for me to basically do something like MyClass * instance = [map NSDictionary properties to MyClass ];? I have a feeling I need to do something with NSCoding or NSKeyedUnarchiver, but rather than stumble through it on my own, I figure someone out there might be able to point me in the right direction.

回答1:

The -setValuesForKeysWithDictionary: method, along with -dictionaryWithValuesForKeys:, is what you want to use.

Example:

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

There is no allKeys on NSObject. You'll need to create an extra category on NSObject like below:

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 

Example:

#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:

Assuming that your class conforms to the Key-Value Coding protocol, you could use the following: (defined as a category on NSDictionary for convenience):

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

And here's how you would use it:

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


回答4:

If your doing this sort of thing chances are your dealing with JSON and you should probably have a look at Mantle https://github.com/Mantle/Mantle

You will then get a convenient method dictionaryValue

[anObject dictionaryValue]; 


回答5:

Just add category for NSObject for getting dictionaryRepresentation from your custom objects (in my case using in JSON serialization only):

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

Also, you can see that my solution will not work with custom objects with nested objects or arrays. For Arrays - just change the lines of code in dictionaryRepresentation method:

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


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!