使用NSMutableDictionary作为属性的后备存储

时间:2011-05-03 18:09:15

标签: iphone objective-c cocoa ios

我正在寻找一种将我的属性直接设置为NSMutableDictionary的简写方法,NSMutableDictionary是一个实例变量。即:

KVCModle.h:

@interface KVModel : NSObject {
    NSMutableDictionary * data;
}
@property(nonatomic,assign)NSString * string1;
@property(nonatomic,assign)NSString * string2;
@end

KVCModel.m

#import "KVModel.h"


@implementation KVModel

-(id)init
{
    self = [super init];
    if(self)
    {
        data = [[NSMutableDictionary alloc] init];
    }
    return self;
}

-(NSString *)string1
{
    return [data objectForKey:@"string1"];
}
-(NSString *)string2
{
    return [data objectForKey:@"string2"];
}
-(void)setString1:(NSString *)_string1
{
    [data setObject:_string1 forKey:@"string1"];
}
-(void)setString2:(NSString *)_string2
{
    [data setObject:_string2 forKey:@"string2"];
}
-(void)dealloc
{
    [data release];
    [super dealloc];
}

@end

我试图覆盖setValue:ForKey:valueForKey:,但是没有调用它们,它们允许您直接设置属性而不使用属性语法。

我已经制作了预处理器宏来完成这项工作,但我对打字并不感兴趣,并且希望将来尽可能多地避免使用它。有没有办法让我不熟悉这项工作?

我已经考虑过使用NSManagedObject,但我不确定我能不能得到我想要的东西。 修改 source

5 个答案:

答案 0 :(得分:8)

如果您尝试使用foo = obj.fooobj.foo = foo等代码访问这些属性,那就是它无效的原因。

属性访问语法与消息语法同义;前者与foo = [obj foo]完全相同,后者与[obj setFoo:foo]完全相同。没有KVC代码可以拦截。属性属于语言级别; KVC处于框架层面。

您需要拦截访问者消息。考虑实现the resolveInstanceMethod: class method,通过使用Objective-C运行时API向类添加方法实现来“解析”选择器。您可以为许多不同的选择器添加相同的实现。

为了您的目的,有一个检查选择器的函数或方法(使用NSStringForSelector和常规NSString检查技术)并返回两个事实:(1)属性名称,以及(2)它是否是一个getter (fooisFoo)或setter(setFoo:)。然后,有两个方法,一个是动态getter,另一个是动态setter。当选择器命名一个getter时,使用dynamic-getter方法添加它;当选择器命名一个setter时,用动态setter方法添加它。

那么dynamic-getter和-setter方法如何工作?他们需要知道动态获取和设置什么属性,但是他们还需要不带参数(getter)或一个参数(setter,它取值),以匹配原始的属性访问消息。您可能想知道这些通用实现如何知道要获取或设置的属性。答案是:它在选择器中!用于发送消息的选择器作为隐藏参数_cmd传递给实现,因此以与以前相同的方式检查该选择器以提取应动态获取或设置的属性的名称。然后,动态获取器应发送[data objectForKey:keyExtractedFromSelector],动态设置器应发送[data setObject:newValue forKey:keyExtractedFromSelector]

两个警告:

  1. 当您使用property-access语法访问未在类@interface中声明的“属性”时,您仍可能会收到编译器的投诉。这是正常的和有意的;你真的应该使用属性访问语法来访问已知的形式属性。你正在做的事情,虽然我发现解决它很有趣,但从技术上讲是滥用属性访问语法。
  2. 这仅适用于对象值。 KVC对原始值进行装箱和拆箱,例如整数;因为KVC没有参与,没有免费拳击和拆箱。如果您已声明正式属性(请参阅1),则需要使用Objective-C运行时API对它们进行内省,并自行装箱并取消装箱。

答案 1 :(得分:6)

这引起了我的好奇心,所以我继续使用Peter Hosey的建议,即压倒+resolveInstanceMethod:以产生吸气剂和制定者。我将结果对象(DDDynamicStorageObject)发布到github存储库:

https://github.com/davedelong/Demos

答案 2 :(得分:3)

您基本上想要的是您自己的NSManagedObject机制实现。我做了类似的事情。看这里:https://gist.github.com/954035 HTH

(更新了代码以消除对不存在的NSString + Utilities.h的依赖性)

(添加缺少的ReleaseAndZero()宏)

答案 3 :(得分:0)

对于所有神圣的爱 - 不要使用NSDictionary作为填充模型对象的所有可能属性的地方。 Ivars更易于调试,并且对其他开发人员(包括您未来的自我)更加清晰。

如果你想使用字典,请使用字典和一些静态定义的键 - 但如果你想要一个模型对象,请使用一些ivars

答案 4 :(得分:0)

我今天和你一样遇到了同样的问题。所以我发现你的问题在这里发布了。

以上使用+resolveInstanceMethod:的答案对我来说有点困难。 :)

我的理解是,只要我们设置属性,我们就会有getter和setter方法,所以我使用setter方法来实现它。

BDLink.h

@property (nonatomic, strong) NSString *type;
@property (nonatomic, strong) NSString *displayName;
@property (nonatomic, strong) NSString *linkURI;

BDLink.m

- (id)initWithLinkInfoDictionary:(NSDictionary *)linkInfoDict {

    for (NSString *key in linkInfoDict) {
        const char *rawName = [key UTF8String];
        NSString *setMethodString = [NSString stringWithFormat:@"set%c%s:", toupper(rawName[0]), (rawName+1)];
        SEL setMethod = NSSelectorFromString(setMethodString);

        if ([self respondsToSelector:setMethod]) {
            [self performSelector:setMethod withObject:linkInfoDict[key]];
        }
    }

    return self;
}

希望它会有所帮助。我的第一个答案是:)