Instantiating Custom Class from NSDictionary

后端 未结 5 685
-上瘾入骨i
-上瘾入骨i 2021-01-30 09:45

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 cus

相关标签:
5条回答
  • 2021-01-30 10:05

    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];
            }
        }
    
    0 讨论(0)
  • 2021-01-30 10:08

    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];
    
    0 讨论(0)
  • 2021-01-30 10:10

    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];
    
    0 讨论(0)
  • 2021-01-30 10:13

    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];
    
    0 讨论(0)
  • 2021-01-30 10:27

    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]];
    
    0 讨论(0)
提交回复
热议问题