使用NSMutableDictionary作为属性的后备存储
我正在寻找一种将我的属性直接设置为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,但我不确定我是否能从中得到我想要的东西。 编辑: 来源
如果您尝试使用foo = obj.foo
和obj.foo = foo
等代码访问属性,那就是它无效的原因。
属性访问语法与消息语法同义; 前者与foo = [obj foo]
完全相同,后者与[obj setFoo:foo]
完全相同。 没有KVC代码可以拦截。 属性属于语言级别; KVC处于框架层面。
您需要拦截访问者消息。 考虑实现resolveInstanceMethod:
class方法 ,在该方法中 ,通过使用Objective-C运行时API向类添加方法实现来“解析”选择器。 您可以为许多不同的选择器添加相同的实现。
为了您的目的,有一个检查选择器的函数或方法(使用NSStringForSelector
和常规的NSString检查技术)并返回两个事实:(1)属性名称,以及(2)它是否是getter( foo
, isFoo
)或setter ( setFoo:
。 然后,有两个方法,一个是动态getter,另一个是动态setter。 当选择器命名一个getter时,使用dynamic-getter方法添加它; 当选择器命名一个setter时,使用dynamic-setter方法添加它。
那么dynamic-getter和-setter方法如何工作呢? 他们需要知道动态获取和设置什么属性,但是他们还需要不带参数(getter)或一个参数(setter,它取值),以匹配原始的属性访问消息。 您可能想知道这些通用实现如何知道要获取或设置的属性。 答案是:它在选择器中! 用于发送消息的选择器作为隐藏参数_cmd
传递给实现,因此以与以前相同的方式检查该选择器以提取应动态获取或设置的属性的名称。 然后,动态getter应该发送[data objectForKey:keyExtractedFromSelector]
,动态setter应该发送[data setObject:newValue forKey:keyExtractedFromSelector]
。
两个警告:
- 当您使用property-access语法访问未在类的
@interface
声明的“属性”时,您仍可能会收到编译器的投诉。 这是正常的和有意的; 你真的只应该使用属性访问语法来访问已知的forms属性。 你正在做什么,虽然我发现解决它很有趣,但从技术上来说是滥用属性访问语法。 - 这仅适用于对象值。 KVC对原始值进行装箱和拆箱,例如整数; 由于KVC没有参与,没有免费拳击和拆箱。 如果您已声明了正式属性(请参阅1),则需要使用Objective-C运行时API对它们进行内省,并自行完成装箱和拆箱。
这激起了我的好奇心,所以我继续使用Peter Hosey关于覆盖+resolveInstanceMethod:
的建议+resolveInstanceMethod:
生成getter和setter。 我将结果对象( DDDynamicStorageObject
)发布到github存储库:
你基本上想要的是你自己的NSManagedObject机器的实现。 我做了类似的事情。 看这里: https: //gist.github.com/954035 HTH
(更新了代码以删除对不存在的NSString + Utilities.h的依赖性)
(添加了缺少的ReleaseAndZero()宏)
为了所有神圣的爱 – 不要使用NSDictionary作为填充模型对象的所有可能属性的地方。 Ivars更易于调试,并且对其他开发人员(包括您未来的自我)更加清晰。
如果要使用字典,请使用字典和一些静态定义的键 – 但如果您需要模型对象,请使用一些ivars
我今天和你一样遇到了同样的问题。 所以我发现你的问题在这里发布了。
上面的答案使用了+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; }
希望它会有所帮助。 我的第一个答案,:)