ObjC valueForKey不适用于NSNumber或UDT

时间:2012-06-11 14:18:06

标签: objective-c nsdictionary objective-c-runtime

我正在尝试将我自己的对象集合深层解析为NSDictionary(对于JSON)。

我有一个基础对象类,我的模型的 所有 扩展,而这个基础对象又扩展了NSObject:

@interface BaseObj : NSObject <DICTMaker>
    - (NSMutableDictionary *) toDICT;
@end

在方法中,我使用objc-runtime来获取属性列表。我可以获得使用[self valueForKey:]的任何属性,我继续向我的字典中插入属性的名称和值。

但是,到目前为止我注意到的是NSNumber和用户定义的类没有添加到字典中!我的解析器最确定地识别它们,因为我把它全部吐出到日志中;但[self valueForKey:]会在所有nil和用户定义的对象上返回NSNumbers

- (NSMutableDictionary *)toDICT {
    NSMutableDictionary *props = [NSMutableDictionary dictionary];
    unsigned int outCount, i;
    objc_property_t *properties = class_copyPropertyList([self class], &outCount);
    for (i = 0; i < outCount; i++) {
        objc_property_t property = properties[i];

        // Both of these work, I promise:
        NSString *propertyName = [NSString stringWithUTF8String:property_getName(property)];
        NSString *propertyType = [NSString stringWithUTF8String:getPropertyType(property)];

        NSLog( @"%@ of Type: %@", propertyName, propertyType );
        id propertyValue = [self valueForKey:propertyName];
        if ( [ propertyValue respondsToSelector:@selector(toDICT:) ] )
            [ props setObject:[propertyValue toDICT] forKey:propertyName ];
        else if ( propertyValue )
            [ props setObject:propertyValue forKey:propertyName ];
        else
            NSLog( @"Unable to get ref to: %@", propertyName );
    }
    free(properties);
    return props;
}

以下是我向创作者投掷的示例对象:

@interface UserRegistrationLocation : BaseObj {
    NSString *address, *street, *street_2, *city;
    NSNumber *addr_state, *addr_zip;
}

@interface UserRegistrationContact : BaseObj {
    NSString *first_name, *last_name;
    NSString *p_phone_area, *p_phone_first_3, *p_phone_last_4;
    NSString *s_phone_area, *s_phone_first_3, *s_phone_last_4;
}

@interface UserRegistration : BaseObj {
    NSString *email, *password, *password_confirm;
    NSNumber *referral;

    UserRegistrationContact *primary, *secondary;
    UserRegistrationLocation *address;
}

NSMutableDictionary *mydict = [myUserRegistration toDICT];

生成的字典仅包含电子邮件,密码和password_confirm的条目:

[11012:f803] Unable to get ref to: referral
[11012:f803] Unable to get ref to: primary
[11012:f803] Unable to get ref to: secondary
[11012:f803] Unable to get ref to: address
[11012:f803] {"user":{"password":"haxme123","password_confirm":"haxme123","email":"my@email.com"}}

请帮助=}!

2 个答案:

答案 0 :(得分:2)

您只为值而非属性声明了ivars。 class_copyPropertyList需要@property声明。您可以使用ivar访问class_getInstanceVariable等功能。

然而,我认为这种方法试图有点过于聪明。我将实现一个返回序列化键的方法,例如-(NSArray *)keysForJSONSerialization。这样可以更清楚地揭示意图,并允许您阻止某些属性的序列化(我怀疑您在某些时候会想要)。

答案 1 :(得分:1)

也许我明白这个问题是错的,但是你的推荐,主要的,所以不仅仅是null? 如果它们不在keyvalue存储区中,您将获得异常。 如果在keyvalue存储中找到属性但指定了nil,则只调用其他部分。至少从您的示例代码中,无法确定是否设置了值。

如果我将您的示例缩减为以下代码,我会得到以下输出     测试值:(null)     无法获得参考:测试     aNumber with Value:5

test为null,会给你的消息“无法......”。 aNumber是正确的。如果我将测试更改为某些文本,则“无法...”部分会消失。此处不会出现非属性的附加成员变量_noProp,因为copyPropertyList仅复制属性。

@interface TestValueForKey : NSObject {
     NSString* _test;
     NSString* _noProp;
     NSNumber* _aNumber;
}

@property (retain) NSString* test;
@property (retain) NSNumber* aNumber;

-(void)myTest;

@implementation TestValueForKey

@synthesize test = _test;
@synthesize aNumber = _aNumber;

-(id)init
{
    if( (self = [super init]) != nil) {
       _test = nil;
       _aNumber = [NSNumber numberWithInt:5];
    }

    return self;
}

-(void)myTest
{
    NSMutableDictionary *props = [NSMutableDictionary dictionary];
    unsigned int outCount, i;
    objc_property_t *properties = class_copyPropertyList([self class], &outCount);
    for( i = 0; i < outCount; i++) {
       objc_property_t property = properties[i];

       // Both of these work, I promise:
       NSString *propertyName = [NSString stringWithUTF8String:property_getName(property)];

      id propertyValue = [self valueForKey:propertyName];
      NSLog( @"%@ with Value: %@", propertyName, propertyValue );
      if ( propertyValue )
          [ props setObject:propertyValue forKey:propertyName ];
      else
          NSLog( @"Unable to get ref to: %@", propertyName );
  }
  free(properties);
}
@end