NSMutableDictionary or Custom Object when adding properties?

  • I need to keep a small (few thousand) record database of sorts. Each record
    has some pre-detertermined fields, but the user can add there own fields to
    to a limited extent. It is a pretty light use so CoreData isn't what I
    really want, plus migrating to future structures is an issue with CoreData.

    This will be simple objects in an NSMutableArray.

    I can use an array of NSMutableDictionaries or perhaps an array of
    class-based custom cocoa objects.

    One nice thing about the object route is that I can have some fields
    (properties) be based on a calculation rather than real storage like they'd
    be in an NSMutableDictionary.

    E.g.

    NSNumber length
    NSNumber width
    NSNumber area

    In an object area could be a method returning length*width instead of an
    instance var for area.

    The trouble comes in the fact that I need to be able to add properties at
    runtime. For the dictionary option, it is easy - just make sure the key
    names don't collide and I can add more keys to each dictionary.

    But for the objects I don't see a nice way to do this

    There is setValue:forUndefinedKey: and then each object could keep a local
    dictionary of these "defined at runtime" keys.

    Thoughts on this?
  • On May 17, 2013, at 12:43 AM, Trygve Inda wrote:

    > I need to keep a small (few thousand) record database of sorts. Each record
    > has some pre-detertermined fields, but the user can add there own fields to
    > to a limited extent. …
    >
    > This will be simple objects in an NSMutableArray.
    >
    > I can use an array of NSMutableDictionaries or perhaps an array of
    > class-based custom cocoa objects.

    Although dictionaries make for a nice quick and dirty object model, I find that they very quickly become more cumbersome than they're worth.  With the amount of help that the compiler is providing these days, it's ridiculously easy to make a small class that does little more than hold some properties.

    > One nice thing about the object route is that I can have some fields
    > (properties) be based on a calculation rather than real storage like they'd
    > be in an NSMutableDictionary.

    Yup, that's the sort of case where the limits of dictionaries show up.

    > The trouble comes in the fact that I need to be able to add properties at
    > runtime. For the dictionary option, it is easy - just make sure the key
    > names don't collide and I can add more keys to each dictionary.
    >
    > But for the objects I don't see a nice way to do this
    >
    > There is setValue:forUndefinedKey: and then each object could keep a local
    > dictionary of these "defined at runtime" keys.

    That seems "nice" enough to me.  The trick is that the custom class has to be sure to only modify the properties via KVC on itself, not the dictionary, in order to maintain KVO compliance.  Another way to put it is that only -setValue:forUndefinedKey: should ever mutate the dictionary (and it should only be invoked by the KVC machinery itself).

    Cheers,
    Ken
  • > On May 17, 2013, at 12:43 AM, Trygve Inda wrote:
    >
    >> I need to keep a small (few thousand) record database of sorts. Each record
    >> has some pre-detertermined fields, but the user can add there own fields to
    >> to a limited extent. …
    >>
    >> This will be simple objects in an NSMutableArray.
    >>
    >> I can use an array of NSMutableDictionaries or perhaps an array of
    >> class-based custom cocoa objects.
    >
    > Although dictionaries make for a nice quick and dirty object model, I find
    > that they very quickly become more cumbersome than they're worth.  With the
    > amount of help that the compiler is providing these days, it's ridiculously
    > easy to make a small class that does little more than hold some properties.
    >
    >
    >> One nice thing about the object route is that I can have some fields
    >> (properties) be based on a calculation rather than real storage like they'd
    >> be in an NSMutableDictionary.
    >
    > Yup, that's the sort of case where the limits of dictionaries show up.
    >
    >
    >> The trouble comes in the fact that I need to be able to add properties at
    >> runtime. For the dictionary option, it is easy - just make sure the key
    >> names don't collide and I can add more keys to each dictionary.
    >>
    >> But for the objects I don't see a nice way to do this
    >>
    >> There is setValue:forUndefinedKey: and then each object could keep a local
    >> dictionary of these "defined at runtime" keys.
    >
    > That seems "nice" enough to me.  The trick is that the custom class has to be
    > sure to only modify the properties via KVC on itself, not the dictionary, in
    > order to maintain KVO compliance.  Another way to put it is that only
    > -setValue:forUndefinedKey: should ever mutate the dictionary (and it should
    > only be invoked by the KVC machinery itself).

    Will that work right if I have an NSTableView and one of the columns has a
    binding to "myCustomProperty" (which is not defined in the object model)...
    Will it get sent:

    [someObject setValue:someValue forUndefinedKey:myCustomProperty]

    Rather than:

    [someObject setMyCustomProperty:someValue];

    On disk the data will be stored in a plist and an array of NSDictionaryies
    (some of the key will be required and predefind) but some will be user
    defined.

    When I load the data from disk if I use dictionary obejects, I don't have to
    do anything else but if I use custom objects I would have to create them and
    send

    [someObject setValue:someValue forUndefinedKey:myCustomProperty]

    To each object.

    Right?

    Thoughts on this?
  • On May 17, 2013, at 1:18 AM, Trygve Inda wrote:

    >> On May 17, 2013, at 12:43 AM, Trygve Inda wrote:
    >>
    >>> The trouble comes in the fact that I need to be able to add properties at
    >>> runtime. For the dictionary option, it is easy - just make sure the key
    >>> names don't collide and I can add more keys to each dictionary.
    >>>
    >>> But for the objects I don't see a nice way to do this
    >>>
    >>> There is setValue:forUndefinedKey: and then each object could keep a local
    >>> dictionary of these "defined at runtime" keys.
    >>
    >> That seems "nice" enough to me.  The trick is that the custom class has to be
    >> sure to only modify the properties via KVC on itself, not the dictionary, in
    >> order to maintain KVO compliance.  Another way to put it is that only
    >> -setValue:forUndefinedKey: should ever mutate the dictionary (and it should
    >> only be invoked by the KVC machinery itself).
    >
    > Will that work right if I have an NSTableView and one of the columns has a
    > binding to "myCustomProperty" (which is not defined in the object model)...
    > Will it get sent:
    >
    > [someObject setValue:someValue forUndefinedKey:myCustomProperty]
    >
    > Rather than:
    >
    > [someObject setMyCustomProperty:someValue];

    Bindings use KVC.  They will do:

    [someObject setValue:someValue forKey:@"myCustomProperty"];

    The KVC machinery is what will eventually call [someObject setValue:someValue forUndefinedKey:@"myCustomProperty"] for the dynamic properties.  (For the predefined properties, it will invoke your setter methods.)

    > On disk the data will be stored in a plist and an array of NSDictionaryies
    > (some of the key will be required and predefind) but some will be user
    > defined.
    >
    > When I load the data from disk if I use dictionary obejects, I don't have to
    > do anything else but if I use custom objects I would have to create them and
    > send
    >
    > [someObject setValue:someValue forUndefinedKey:myCustomProperty]
    >
    > To each object.
    >
    > Right?

    You can use -setValuesForKeysWithDictionary: to set all of the properties of your object from a dictionary.  That uses KVC on each key-value pair, so it works like I described above for bindings.

    That said, you should consider doing proper keyed archiving of your class.  Don't forget the limits of property lists.

    Regards,
    Ken
  • On 17/05/2013, at 3:43 PM, Trygve Inda <cocoadev...> wrote:

    > But for the objects I don't see a nice way to do this
    >
    > There is setValue:forUndefinedKey: and then each object could keep a local
    > dictionary of these "defined at runtime" keys.

    There are low-level runtime methods that allow you to add properties at runtime. I'm not sure whether that's considered good practice, but it's doable.

    Otherwise your approach seems fine.

    --Graham
  • > On May 17, 2013, at 1:18 AM, Trygve Inda wrote:
    >
    >>> On May 17, 2013, at 12:43 AM, Trygve Inda wrote:
    >>>
    >>>> The trouble comes in the fact that I need to be able to add properties at
    >>>> runtime. For the dictionary option, it is easy - just make sure the key
    >>>> names don't collide and I can add more keys to each dictionary.
    >>>>
    >>>> But for the objects I don't see a nice way to do this
    >>>>
    >>>> There is setValue:forUndefinedKey: and then each object could keep a local
    >>>> dictionary of these "defined at runtime" keys.
    >>>
    >>> That seems "nice" enough to me.  The trick is that the custom class has to
    >>> be
    >>> sure to only modify the properties via KVC on itself, not the dictionary, in
    >>> order to maintain KVO compliance.  Another way to put it is that only
    >>> -setValue:forUndefinedKey: should ever mutate the dictionary (and it should
    >>> only be invoked by the KVC machinery itself).
    >>
    >> Will that work right if I have an NSTableView and one of the columns has a
    >> binding to "myCustomProperty" (which is not defined in the object model)...
    >> Will it get sent:
    >>
    >> [someObject setValue:someValue forUndefinedKey:myCustomProperty]
    >>
    >> Rather than:
    >>
    >> [someObject setMyCustomProperty:someValue];
    >
    > Bindings use KVC.  They will do:
    >
    > [someObject setValue:someValue forKey:@"myCustomProperty"];
    >
    > The KVC machinery is what will eventually call [someObject setValue:someValue
    > forUndefinedKey:@"myCustomProperty"] for the dynamic properties.  (For the
    > predefined properties, it will invoke your setter methods.)
    >
    >
    >> On disk the data will be stored in a plist and an array of NSDictionaryies
    >> (some of the key will be required and predefind) but some will be user
    >> defined.
    >>
    >> When I load the data from disk if I use dictionary obejects, I don't have to
    >> do anything else but if I use custom objects I would have to create them and
    >> send
    >>
    >> [someObject setValue:someValue forUndefinedKey:myCustomProperty]
    >>
    >> To each object.
    >>
    >> Right?
    >
    > You can use -setValuesForKeysWithDictionary: to set all of the properties of
    > your object from a dictionary.  That uses KVC on each key-value pair, so it
    > works like I described above for bindings.
    >
    > That said, you should consider doing proper keyed archiving of your class.
    > Don't forget the limits of property lists.
    >
    > Regards,
    > Ken
    >
    >

    So do my user-added properties (that I will store in a dictionary owned by
    my custom-class object) have to begin with a lowercase letter to have the
    KVC perform correctly?

    E.g myCustomField vs MyCustomField?

    Of course with the direct dictionary method it would not matter.

    Thanks
  • If you go the dictionary route a simple category on NSDictionary would allow you to compute dynamic properties such as area = length * width.

    Sandor Szatmari

    On May 17, 2013, at 1:43, Trygve Inda <cocoadev...> wrote:

    > I need to keep a small (few thousand) record database of sorts. Each record
    > has some pre-detertermined fields, but the user can add there own fields to
    > to a limited extent. It is a pretty light use so CoreData isn't what I
    > really want, plus migrating to future structures is an issue with CoreData.
    >
    > This will be simple objects in an NSMutableArray.
    >
    > I can use an array of NSMutableDictionaries or perhaps an array of
    > class-based custom cocoa objects.
    >
    > One nice thing about the object route is that I can have some fields
    > (properties) be based on a calculation rather than real storage like they'd
    > be in an NSMutableDictionary.
    >
    > E.g.
    >
    > NSNumber length
    > NSNumber width
    > NSNumber area
    >
    > In an object area could be a method returning length*width instead of an
    > instance var for area.
    >
    > The trouble comes in the fact that I need to be able to add properties at
    > runtime. For the dictionary option, it is easy - just make sure the key
    > names don't collide and I can add more keys to each dictionary.
    >
    > But for the objects I don't see a nice way to do this
    >
    > There is setValue:forUndefinedKey: and then each object could keep a local
    > dictionary of these "defined at runtime" keys.
    >
    > Thoughts on this?
  • Alternatively, a dictionary mapping keys onto blocks of type (void(^)(void)), which each compute their result.  That, combined with typedef void(^voidBlock)(void); voidBlock constant(id r) { return [^{return r;} copy]; } would give you a dictionary that can store both constants and computed values.

    Bob

    On 17 May 2013, at 12:07, Sandor Szatmari <admin.szatmari.net...> wrote:

    > If you go the dictionary route a simple category on NSDictionary would allow you to compute dynamic properties such as area = length * width.
    >
    > Sandor Szatmari
    >
    > On May 17, 2013, at 1:43, Trygve Inda <cocoadev...> wrote:
    >
    >> I need to keep a small (few thousand) record database of sorts. Each record
    >> has some pre-detertermined fields, but the user can add there own fields to
    >> to a limited extent. It is a pretty light use so CoreData isn't what I
    >> really want, plus migrating to future structures is an issue with CoreData.
    >>
    >> This will be simple objects in an NSMutableArray.
    >>
    >> I can use an array of NSMutableDictionaries or perhaps an array of
    >> class-based custom cocoa objects.
    >>
    >> One nice thing about the object route is that I can have some fields
    >> (properties) be based on a calculation rather than real storage like they'd
    >> be in an NSMutableDictionary.
    >>
    >> E.g.
    >>
    >> NSNumber length
    >> NSNumber width
    >> NSNumber area
    >>
    >> In an object area could be a method returning length*width instead of an
    >> instance var for area.
    >>
    >> The trouble comes in the fact that I need to be able to add properties at
    >> runtime. For the dictionary option, it is easy - just make sure the key
    >> names don't collide and I can add more keys to each dictionary.
    >>
    >> But for the objects I don't see a nice way to do this
    >>
    >> There is setValue:forUndefinedKey: and then each object could keep a local
    >> dictionary of these "defined at runtime" keys.
    >>
    >> Thoughts on this?

  • On May 16, 2013, at 11:41 PM, Graham Cox <graham.cox...> wrote:

    >> There is setValue:forUndefinedKey: and then each object could keep a local
    >> dictionary of these "defined at runtime" keys.
    >
    > There are low-level runtime methods that allow you to add properties at runtime. I'm not sure whether that's considered good practice, but it's doable.

    MYDynamicObject is a class I wrote that enables this:
    https://github.com/snej/MYUtilities/blob/master/MYDynamicObject.h
    You can subclass this, add properties to the @interface, and then mark them as @dynamic in the @implementation. Then you just need to implement a generic getter and setter method to do something like store the value in an internal mutable dictionary.

    (This is just like what NSManagedObject does with @dynamic properties, only in a more generic form.)

    —Jens
  • Howdy,

    It's very easy to do using runtime. I have a framework for doing dynamic creation at runtime. Also, It's easy to get O-C based class elements such as properties, ivars, protocols. methods using runtime. You can take a look at this.
    https://gist.github.com/Ch0c0late/5575679 It introspects protocol conformed by a class & also it could add new protocol to the class. The rest of the code is on my local. You can feel free using it. It's part of a SCK framework. If you need more type e.g. methods, properties, & etc, you can take a look at my framework. I'm coding an OODBMS for OS X that uses O-C runtime so much. Again using runtime it's easy. How?
    First of all the system creates an empty array. Then the user can create any type of object he/she wants without any limit. For archiving & unarchiving I used Boxing & UnBoxing classes(NSNumber, NSValue, NSNull) that let me to archive c primitive types and so on. There is s.t. interesting that could be your answer. Take a look at this one https://github.com/snej/MYUtilities/blob/master/MYDynamicObject.h It's a good class that you can use to create dynamic object at runtime. I added Archiving & UnArchiving mechanism to it for my own. Please note that in 64 bit system there is no need to declare iVars. The runtime acts as iVar & property with property.

    Down the hatch.
  • >
    > On May 16, 2013, at 11:41 PM, Graham Cox <graham.cox...> wrote:
    >
    >>> There is setValue:forUndefinedKey: and then each object could keep a local
    >>> dictionary of these "defined at runtime" keys.
    >>
    >> There are low-level runtime methods that allow you to add properties at
    >> runtime. I'm not sure whether that's considered good practice, but it's
    >> doable.
    >
    > MYDynamicObject is a class I wrote that enables this:
    > https://github.com/snej/MYUtilities/blob/master/MYDynamicObject.h
    > You can subclass this, add properties to the @interface, and then mark them as
    > @dynamic in the @implementation. Then you just need to implement a generic
    > getter and setter method to do something like store the value in an internal
    > mutable dictionary.
    >
    > (This is just like what NSManagedObject does with @dynamic properties, only in
    > a more generic form.)
    >
    > —Jens

    How is this really any different than using setValue:forUndefinedKey?
  • On May 17, 2013, at 1:43 PM, Trygve Inda <cocoadev...> wrote:

    > How is this really any different than using setValue:forUndefinedKey?

    If you’re just going to access properties through KVC it’s not. Maybe that’s all the OP needs? What it gives you is actual ObjC-level properties that you can get and set directly in code.

    —Jens
previous month may 2013 next month
MTWTFSS
    1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31    
Go to today