开发者

Using NSMutableDictionary as backing store for properties

I am looking for a shorthand way of setting my properties directly to an NSMutableDictionary that is a instance variable. ie:

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 deall开发者_开发知识库oc];
}

@end

I have tried to override setValue:ForKey: and valueForKey:, but those aren't called, they allow you to directly set properties without using the property syntax.

I have made preprocessor macros to make this work in the past, but I am not interested in typing at all, and would like to avoid as much of it as I can in the future. Is there a way to make this work that I am not familiar with?

I have thought about using NSManagedObject, but I am not sure if I can get what I want out of that. EDIT: source


If you're trying to access the properties with code like foo = obj.foo and obj.foo = foo, that's why it doesn't work.

Property-access syntax is synonymous with message syntax; the former is exactly the same as foo = [obj foo], and the latter is exactly the same as [obj setFoo:foo]. There is no KVC code to intercept. Properties are at the language level; KVC is at the framework level.

You'll need to intercept the accessor messages instead. Consider implementing the resolveInstanceMethod: class method, in which you “resolve” the selector by adding a method implementation to the class using the Objective-C runtime API. You can add the same implementation(s) for many different selectors.

For your purpose, have a function or method that examines the selector (using NSStringForSelector and regular NSString-examining techniques) and returns two facts: (1) the property name, and (2) whether it's a getter (foo, isFoo) or setter (setFoo:). Then, have two more methods, one a dynamic getter and the other a dynamic setter. When the selector names a getter, add it with your dynamic-getter method; when the selector names a setter, add it with your dynamic-setter method.

So how do the dynamic-getter and -setter methods work? They'll need to know what property to dynamically get and set, but they also need to take no arguments (getter) or one argument (setter, which takes the value), in order to match the original property-access message. You might be wondering how these generic implementations can know what property to get or set. The answer is: It's in the selector! The selector used to send the message is passed to the implementation as the hidden argument _cmd, so examine that selector the same way as before to extract the name of the property you should dynamically get or set. Then, the dynamic getter should send [data objectForKey:keyExtractedFromSelector] and the dynamic setter should send [data setObject:newValue forKey:keyExtractedFromSelector].

Two caveats:

  1. You may still get complaints from the compiler when you use the property-access syntax to access a “property” that you have not declared in the class's @interface. This is normal and intentional; you're really only supposed to use property-access syntax to access known formal properties. What you're doing, while I found it fun to solve, is technically an abuse of the property-access syntax.
  2. This will only work for object values. KVC does the boxing and unboxing for primitive values, such as integers; since KVC is not involved, no free boxing and unboxing. If you have declared formal properties (see 1), you'll need to introspect them using the Objective-C runtime API, and do the boxing and unboxing yourself with your findings.


This piqued my curiosity, so I went ahead and used Peter Hosey's suggestion of overriding +resolveInstanceMethod: to generate the getters and setters. I posted the resulting object (DDDynamicStorageObject) to a github repository:

https://github.com/davedelong/Demos


What you basically want is your own implementation of the NSManagedObject machinery. I have done something similar. Look here: https://gist.github.com/954035 HTH

(Updated the code to remove the dependency on the non-existant NSString+Utilities.h)

(Added missing ReleaseAndZero() macro)


For the love of all that is sacred - do not use an NSDictionary as a place to stuff every conceivable property of a model object. Ivars are easier to debug, and much much clearer to other developers (including your future self).

If you want to use a dictionary, use a dictionary and some statically defined keys - but if you want a model object, use some ivars


I come to the same problem today just like you. So I find your question posted here.

The answers above used the +resolveInstanceMethod: is a little bit hard for me. :)

My understanding is that as long as we setup the property, we would have getter and setter method, so I use the setter method to implement it.

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

Hope it would be helpful. My first answer, :)

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜