从 NSDictionary 实例化自定义类

Posted

技术标签:

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

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

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

【问题讨论】:

【参考方案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];
        
    

【讨论】:

以上是关于从 NSDictionary 实例化自定义类的主要内容,如果未能解决你的问题,请参考以下文章

以编程方式实例化自定义视图控制器并通过标签获取子视图

使用核心数据实例化自定义对象

php学习日记1(关于Thinkphp框架中D的含义和用法)

在界面生成器中使用现有的自定义 UIView

Sinatra/Rails:在应用程序生命周期内保持自定义类实例

自定义异常