【问题标题】:Instantiating Custom Class from NSDictionary从 NSDictionary 实例化自定义类
【发布时间】:2009-05-06 17:03:53
【问题描述】:

我觉得这是个愚蠢的问题,但我还是会问...

我有一个NSDictionary 对象的集合,它们的键/值对对应于我创建的自定义类,称之为MyClass。有没有一种简单或“最佳实践”的方法可以让我基本上做类似MyClass * instance = [map NSDictionary 属性到MyClass ]; 的事情?我觉得我需要对 NSCodingNSKeyedUnarchiver 做点什么,但与其自己跌跌撞撞,我想有人可以为我指明正确的方向。

【问题讨论】:

    标签: iphone objective-c collections nsdictionary


    【解决方案1】:

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

    【讨论】:

    • 这很方便,而 setValuesForPropertiesWithKeys 就是要走的路。它和我的代码完全一样,而且它是内置的!很好的发现。
    • 这是一个绝妙的方法。将它与 objc_* API 结合使用,您可以构建一个自动序列化类(这样您就可以停止编写那些繁琐的 -initWithCoder: 和 -encodeWithCoder: 方法)
    • 太棒了。这会派上用场的。
    • @Red:谢谢!是的,我希望找到一种“一体化”的方式来简洁地做到这一点。您能否详细说明或指出一些关于 objc_* API 的更多解释?
    • initWithProperties,你是意思说`[self setValuesForKeysWithDictionary:**properties**];`??
    【解决方案2】:

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

    【讨论】:

    • 你不妨在这个类别中添加另一个方法: - (NSDictionary*) dictionaryWithValuesForKeys { return [self dictionaryWithValuesForKeys:[self allKeys]]; }
    【解决方案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];
    

    【讨论】:

    • 谢谢。这就是我要找的。 :)
    • 看起来 Red 的答案更好。我建议您将接受的答案切换为他的:)
    • 完成。 :) 不过,您的回答有助于让我思考这需要如何完成。
    • 谁能指出我如何使我的自定义类(即 UIViewController 的子类)符合Key-Value coding 协议的正确方向?
    【解决方案4】:

    如果你做这种事情的机会是你处理 JSON,你可能应该看看 Mantle https://github.com/Mantle/Mantle

    你会得到一个方便的方法dictionaryValue

    [anObject dictionaryValue];
    

    【讨论】:

      【解决方案5】:

      只需为 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];
              }
          }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-01-25
        • 1970-01-01
        • 1970-01-01
        • 2019-01-29
        • 1970-01-01
        • 2012-11-30
        • 2012-04-26
        相关资源
        最近更新 更多