NSDictionary as backing store to Properties

Discussion in 'iPhone/iPad Programming' started by faffoo, Aug 2, 2011.

  1. macrumors regular

    Joined:
    May 22, 2008
    Location:
    Glasgow, UK
    #1
    Hi there,

    I have an NSDictionary as an instance variable, and want to be able to access the values in it by properties (dot syntax).

    I am aware you cannot subclass NSDictionary, so I realised I need to create a new class and create properties on it.

    So if in my header file I have:
    Code:
    @interface MyTest : NSObject
    @property (nonatomic, readonly) NSString *name;
    @end
    
    Is there any easy way to intercept this request (i.e. something = myObject.name; or myObject.name = something) before an unrecognised selector is thrown.

    My usage of this would typically be:
    Code:
    -(NSString *)name {
    return [dictionary objectForKey:@"name"];
    }
    but without having to create a method for every signature. All help appreciated. Kind regards
    Matthew Casey
     
  2. robbieduncan, Aug 2, 2011
    Last edited: Aug 2, 2011

    Moderator emeritus

    robbieduncan

    Joined:
    Jul 24, 2002
    Location:
    London
    #2
    The answer to this is maybe. I've not tried this an, in all honesty, I'm not sure if it's a good idea. But you should certainly give it a go :D

    First of all if you are not going to actually provide the name and setName (for example) methods you will probably want to look at overriding respondsToSelector:. Once you have done that so your instances look like they respond to the selectors you want you can probably use the exceptionally sketchy sounding resolveInstanceMethod: to point all these selectors at a single method in your code.

    I think we can get around the issue of not wanting to code accessors for each and every property using @dynamic

    Now the next bit is all typed straight into this window so probably won't work. But it shows you what I'm thinking.

    .h
    Code:
    @interface MyTest : NSObject
    {
    NSMutableDictionary *properties;
    }
    @property (nonatomic, readonly) NSString *name;
    @end
    
    .m
    Code:
    // Assume normal init to create/load the propeties array
    
    @dynamic(name);
    
    - (BOOL)respondsToSelector:(SEL)aSelector
    {
    if (aSelector == @selector(name))
    {
    return YES;
    }
    [super respondsToSelector:aSelector];
    }
    
    - (id)dictionaryValueForKey:(NSString *) key
    {
    return [properties objectForKey:key];
    }
    
    // This is the function that will handle the dictionary access for get mode
    id dynamicGet(id self, SEL _cmd)
    {
    return [self dictionaryValueForKey:NSStringFromSelector(_cmd)];
    }
    
    + (BOOL)resolveInstanceMethod:(SEL)aSel
    {
    if (aSel == @selector(name))
    {
    class_addMethod([self class], aSEL, (IMP) dynamicGet, "@@:"); // See http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html#//apple_ref/doc/uid/TP40008048-CH100 for type encodings.  This says the method returns an object and has two parameters and object and a SEL
    return YES;
    }
    return [super resolveInstanceMethod:aSel];
    }
    
    Note that the keys in the properties dictionary must match the pseudo property names exactly. So in this case the key must be @"name". The set version would be a little more complicated as you would have to have a different method signature and remove the set from the selector.

    You could make it a bit more robust by, for example, always lower-casing the key so you could use .Name or .NAME and still hit the @"name" key in the dictionary.
     

Share This Page