How to implement an object whose properties are really dictionary entries.

  • Hi. I guess many have already stumped into this one, but my wildest phrasing attempts in web-searches didn't yield.

    I'd like to have a "data" object, similar to (but much simpler than) CoreData entity, which only exports a bunch of properties. Something like

    @interface MSPersonalData :NSObject
    {
    }
    -(id) initWithData:(NSDictionary*)initialData;
    -(id) init;
    @property (nonatomic, retain) NSString* firstName;
    @property (nonatomic, retain) NSString* lastName;
    @property (nonatomic, retain) NSDate*  birthDate;
    @property (nonatomic, retain) NSNumber* salary;
    @end

    I'd like this object to have NO ivars, and NO property implementations, not even synthesized. Instead I'd like the object to either inherit from NSMutableDictionary or own a single NSMutableDictionary ivar, and I'd like any access to my object to be transparently translated to internal dictionary valueForKey: and setValue:forKey:.

    Simply put, I'd like code like this:

    MSPersonalData *myDataObject = ;[MSPersonalData alloc] init];
    myDataObject.firstName = @"Motti";

    to actually perform

    [self  setValue:@"Motti" forKey: @"firstName"];    // if MSPersonalData inherits NSMutableDictionary
    or
    [internalDictionary  setValue:@"Motti" forKey: @"firstName"]; // if MSPersonalData owns  NSMutableDictionary.

    Such object can be very useful for UI binding,  and as self-documented API for a sub-system. Instead of declaring a set of static keys as string, just behave as if there were iVars per each key of the object. I think CoreData NSManagedObjects do this, in their "primitive" implementation.

    I tried several schemes for implementation, but all became cumbersome, and I had to do much trickery, and in the end it looked ugly. I strongly feel there must be a simple and elegant way to do this. To have a mutable dictionary react to the "syntactic sugar" of dot notation.

    Is there a good, general solution for this?

    Motti Shneor,
    Spectrum Reflections Ltd.
  • On Jul 11, 2012, at 6:34 AM, Motti Shneor wrote:

    > Hi. I guess many have already stumped into this one, but my wildest phrasing attempts in web-searches didn't yield.
    >
    > I'd like to have a "data" object, similar to (but much simpler than) CoreData entity, which only exports a bunch of properties. Something like
    >
    > @interface MSPersonalData :NSObject
    > {
    > }
    > -(id) initWithData:(NSDictionary*)initialData;
    > -(id) init;
    > @property (nonatomic, retain) NSString* firstName;
    > @property (nonatomic, retain) NSString* lastName;
    > @property (nonatomic, retain) NSDate*  birthDate;
    > @property (nonatomic, retain) NSNumber* salary;
    > @end
    >
    > I'd like this object to have NO ivars, and NO property implementations, not even synthesized. Instead I'd like the object to either inherit from NSMutableDictionary or own a single NSMutableDictionary ivar, and I'd like any access to my object to be transparently translated to internal dictionary valueForKey: and setValue:forKey:.
    >
    > Simply put, I'd like code like this:
    >
    > MSPersonalData *myDataObject = ;[MSPersonalData alloc] init];
    > myDataObject.firstName = @"Motti";
    >
    > to actually perform
    >
    > [self  setValue:@"Motti" forKey: @"firstName"];    // if MSPersonalData inherits NSMutableDictionary
    > or
    > [internalDictionary  setValue:@"Motti" forKey: @"firstName"]; // if MSPersonalData owns  NSMutableDictionary.
    >
    > Such object can be very useful for UI binding,  and as self-documented API for a sub-system. Instead of declaring a set of static keys as string, just behave as if there were iVars per each key of the object. I think CoreData NSManagedObjects do this, in their "primitive" implementation.
    >
    > I tried several schemes for implementation, but all became cumbersome, and I had to do much trickery, and in the end it looked ugly. I strongly feel there must be a simple and elegant way to do this. To have a mutable dictionary react to the "syntactic sugar" of dot notation.
    >
    > Is there a good, general solution for this?

    What is hampering you, it seems to me, is your requirement to use dot syntax. At least, to use dot syntax and not get a bunch of compiler warnings. Even Core Data still requires you to declare properties to avoid compiler warnings, which is a good sign that what you are after is not "cleanly" possible.

    HTH,

    Keary Suska
    Esoteritech, Inc.
    "Demystifying technology for your home or business"
  • It sounds like the OP is looking for a model object that uses an NSMutableDictionary as the backing store for its properties, as opposed to individual ivars. You can do this.

    You declare all your properties, and you declare a single NSMutableDictionary ivar. All of the properties should be implemented as @dynamic in the .m file.

    Since the properties are dynamic, the compiler will not generate implementations for them. You'll have to do that yourself at runtime. Fortunately, if you know what you're doing, that's not too hard.

    You'll have to override +resolveInstanceMethod: to figure out what the user is trying to do: get or set. You'll use the selector passed in to derive the key under which you're storing the data in the dictionary. With this information, you construct a block of the appropriate signature, capturing whatever information you need, and turn it into an IMP using imp_implementationWithBlock(). You can then add that method to the class using class_addMethod().

    So yes, this is totally possible. However, it seems like it'll be a lot of work if all you're doing is trying to save yourself from implementing a few setters and getters.

    Dave

    Sent from Jane

    On 2012-07-11, at 6:46 AM, Keary Suska <cocoa-dev...> wrote:

    > On Jul 11, 2012, at 6:34 AM, Motti Shneor wrote:
    >
    >> Hi. I guess many have already stumped into this one, but my wildest phrasing attempts in web-searches didn't yield.
    >>
    >> I'd like to have a "data" object, similar to (but much simpler than) CoreData entity, which only exports a bunch of properties. Something like
    >>
    >> @interface MSPersonalData :NSObject
    >> {
    >> }
    >> -(id) initWithData:(NSDictionary*)initialData;
    >> -(id) init;
    >> @property (nonatomic, retain) NSString* firstName;
    >> @property (nonatomic, retain) NSString* lastName;
    >> @property (nonatomic, retain) NSDate*  birthDate;
    >> @property (nonatomic, retain) NSNumber* salary;
    >> @end
    >>
    >> I'd like this object to have NO ivars, and NO property implementations, not even synthesized. Instead I'd like the object to either inherit from NSMutableDictionary or own a single NSMutableDictionary ivar, and I'd like any access to my object to be transparently translated to internal dictionary valueForKey: and setValue:forKey:.
    >>
    >> Simply put, I'd like code like this:
    >>
    >> MSPersonalData *myDataObject = ;[MSPersonalData alloc] init];
    >> myDataObject.firstName = @"Motti";
    >>
    >> to actually perform
    >>
    >> [self  setValue:@"Motti" forKey: @"firstName"];    // if MSPersonalData inherits NSMutableDictionary
    >> or
    >> [internalDictionary  setValue:@"Motti" forKey: @"firstName"]; // if MSPersonalData owns  NSMutableDictionary.
    >>
    >> Such object can be very useful for UI binding,  and as self-documented API for a sub-system. Instead of declaring a set of static keys as string, just behave as if there were iVars per each key of the object. I think CoreData NSManagedObjects do this, in their "primitive" implementation.
    >>
    >> I tried several schemes for implementation, but all became cumbersome, and I had to do much trickery, and in the end it looked ugly. I strongly feel there must be a simple and elegant way to do this. To have a mutable dictionary react to the "syntactic sugar" of dot notation.
    >>
    >> Is there a good, general solution for this?
    >
    >
    > What is hampering you, it seems to me, is your requirement to use dot syntax. At least, to use dot syntax and not get a bunch of compiler warnings. Even Core Data still requires you to declare properties to avoid compiler warnings, which is a good sign that what you are after is not "cleanly" possible.
    >
    > HTH,
    >
    > Keary Suska
    > Esoteritech, Inc.
    > "Demystifying technology for your home or business"
  • On 2012-07-11, at 10:03 AM, Dave DeLong <davedelong...> wrote:

    > It sounds like the OP is looking for a model object that uses an NSMutableDictionary as the backing store for its properties, as opposed to individual ivars. You can do this.
    >
    > You declare all your properties, and you declare a single NSMutableDictionary ivar. All of the properties should be implemented as @dynamic in the .m file.
    >
    > Since the properties are dynamic, the compiler will not generate implementations for them. You'll have to do that yourself at runtime. Fortunately, if you know what you're doing, that's not too hard.
    >
    > You'll have to override +resolveInstanceMethod: to figure out what the user is trying to do: get or set. You'll use the selector passed in to derive the key under which you're storing the data in the dictionary. With this information, you construct a block of the appropriate signature, capturing whatever information you need, and turn it into an IMP using imp_implementationWithBlock(). You can then add that method to the class using class_addMethod().
    >

    Another way would be to override the invocation forwarding mechanism (-forwardInvocation: and -methodSignatureForSelector:) to funnel all the messages through a single getter/setter; this would (possibly) mean that less code is generated at runtime, perhaps at the expense of some added complexity and performance degradation.

    I think, however, that the best solution here is to just adopt a newer compiler; recent versions of LLVM are capable of automatically synthesizing properties[1], and provide support for both literals and boxed expressions[2], which make initializing and using both NSArray and NSDictionary much closer to what the OP was looking for.

    —Mt.

    [1]: “Objective-C Autosynthesis of Properties” in http://clang.llvm.org/docs/LanguageExtensions.html#objc_lambdas
    [2]: http://clang.llvm.org/docs/ObjectiveCLiterals.html
  • On Jul 11, 2012, at 8:03 AM, Dave DeLong wrote:

    > It sounds like the OP is looking for a model object that uses an NSMutableDictionary as the backing store for its properties, as opposed to individual ivars. You can do this.
    >
    > You declare all your properties, and you declare a single NSMutableDictionary ivar. All of the properties should be implemented as @dynamic in the .m file.
    >
    > Since the properties are dynamic, the compiler will not generate implementations for them. You'll have to do that yourself at runtime. Fortunately, if you know what you're doing, that's not too hard.
    >
    > You'll have to override +resolveInstanceMethod: to figure out what the user is trying to do: get or set. You'll use the selector passed in to derive the key under which you're storing the data in the dictionary. With this information, you construct a block of the appropriate signature, capturing whatever information you need, and turn it into an IMP using imp_implementationWithBlock(). You can then add that method to the class using class_addMethod().
    >
    > So yes, this is totally possible. However, it seems like it'll be a lot of work if all you're doing is trying to save yourself from implementing a few setters and getters.

    Sorry, I misread. Since the OP doesn't care about pre-declaring all the properties, then the most elegant answer is, "use Core Data." Why reinvent the wheel? All the functionality is already there, and then some.

    OP: If for whatever reason you feel that you need to reinvent the wheel, or you need your own backing store that isn't CD-friendly, it seems much simpler to me to just override the "undefinedKey" methods.

    Keary Suska
    Esoteritech, Inc.
    "Demystifying technology for your home or business"

    > On 2012-07-11, at 6:46 AM, Keary Suska <cocoa-dev...> wrote:
    >
    >> On Jul 11, 2012, at 6:34 AM, Motti Shneor wrote:
    >>
    >>> Hi. I guess many have already stumped into this one, but my wildest phrasing attempts in web-searches didn't yield.
    >>>
    >>> I'd like to have a "data" object, similar to (but much simpler than) CoreData entity, which only exports a bunch of properties. Something like
    >>>
    >>> @interface MSPersonalData :NSObject
    >>> {
    >>> }
    >>> -(id) initWithData:(NSDictionary*)initialData;
    >>> -(id) init;
    >>> @property (nonatomic, retain) NSString* firstName;
    >>> @property (nonatomic, retain) NSString* lastName;
    >>> @property (nonatomic, retain) NSDate*  birthDate;
    >>> @property (nonatomic, retain) NSNumber* salary;
    >>> @end
    >>>
    >>> I'd like this object to have NO ivars, and NO property implementations, not even synthesized. Instead I'd like the object to either inherit from NSMutableDictionary or own a single NSMutableDictionary ivar, and I'd like any access to my object to be transparently translated to internal dictionary valueForKey: and setValue:forKey:.
    >>>
    >>> Simply put, I'd like code like this:
    >>>
    >>> MSPersonalData *myDataObject = ;[MSPersonalData alloc] init];
    >>> myDataObject.firstName = @"Motti";
    >>>
    >>> to actually perform
    >>>
    >>> [self  setValue:@"Motti" forKey: @"firstName"];    // if MSPersonalData inherits NSMutableDictionary
    >>> or
    >>> [internalDictionary  setValue:@"Motti" forKey: @"firstName"]; // if MSPersonalData owns  NSMutableDictionary.
    >>>
    >>> Such object can be very useful for UI binding,  and as self-documented API for a sub-system. Instead of declaring a set of static keys as string, just behave as if there were iVars per each key of the object. I think CoreData NSManagedObjects do this, in their "primitive" implementation.
    >>>
    >>> I tried several schemes for implementation, but all became cumbersome, and I had to do much trickery, and in the end it looked ugly. I strongly feel there must be a simple and elegant way to do this. To have a mutable dictionary react to the "syntactic sugar" of dot notation.
    >>>
    >>> Is there a good, general solution for this?
    >>
    >>
    >> What is hampering you, it seems to me, is your requirement to use dot syntax. At least, to use dot syntax and not get a bunch of compiler warnings. Even Core Data still requires you to declare properties to avoid compiler warnings, which is a good sign that what you are after is not "cleanly" possible.
  • On Jul 11, 2012, at 7:03 AM, Dave DeLong <davedelong...> wrote:

    > You'll have to override +resolveInstanceMethod: to figure out what the user is trying to do: get or set. You'll use the selector passed in to derive the key under which you're storing the data in the dictionary. With this information, you construct a block of the appropriate signature, capturing whatever information you need, and turn it into an IMP using imp_implementationWithBlock(). You can then add that method to the class using class_addMethod().

    I implemented this as part of the CouchCocoa library (and yes, it is pretty tricky). I tried to factor it so the base class is generic, and should be exactly what the OP is looking for:

    https://github.com/couchbaselabs/CouchCocoa/blob/master/Model/CouchDynamicO
    bject.h

    https://github.com/couchbaselabs/CouchCocoa/blob/master/Model/CouchDynamicO
    bject.m


    —Jens
  • Wow, and thanks, everyone. BTW, what does "OP" stand for? (obviously, its me...)

    Of what I read from everyone, and after examining the suggested code from Jens Alfke, I think I'm inclined to something simpler, hinted by  Keary Suska.  Could you spare a few more words on the "undefinedKey" override?

    Obviously, I would use CoreData if I could. In fact, we use CoreData for the base model of our application. However, there is another "layer" higher than the CoreData model, that consists of intermediate data objects that serve as mediators between server messages and our "real" model objects in CoreData. The "Keys" or property-names are dictated by server definitions, and do not fully match those we have in our CoreData model.

    We don't want these objects to be stored, and we don't want them to be dependent on some managedObjectContext (for threading reasons). Just simple, old-style, model objects.

    I'd like to avoid dynamic auto-generation of methods at runtime. I can't see why this is needed --- ALL my getters and setters will look exactly the same.

    We have dozens such "data classes", and I hate to copy-paste zillions of exactly-the-same implementations by hand.  For every property (e.g. "firstName") I would need to to write

    - (NSString *)firstName {
    return [internalMutableDict valueForKey:firstName];
    }
    - (void) setFirstName :(NSString *)firstName {
    [internalMutableDict setValue:firstName forKey:@"firstName"];
    }

    Isn't writing 1000 such implementations error-prone? call for automation of some kind?

    The reason we insist on dot-notation for accessing these objects, is that the code which does this, is written in C++ or C style, using structs and dot notation. That code could compile with no change against C++ data objects (in Windows) or against my Propery-based data objects for Mac.

    So... It's not that we're anti-CD, on the contrary.... only this is a big project with lot's of legacy shared code imposed on the system, to say nothing of legacy thinking  and "shared thoughts" by management.

    On 11 ביול 2012, at 17:25, Keary Suska wrote:

    > On Jul 11, 2012, at 8:03 AM, Dave DeLong wrote:
    >
    >> It sounds like the OP is looking for a model object that uses an NSMutableDictionary as the backing store for its properties, as opposed to individual ivars. You can do this.
    >>
    >> You declare all your properties, and you declare a single NSMutableDictionary ivar. All of the properties should be implemented as @dynamic in the .m file.
    >>
    >> Since the properties are dynamic, the compiler will not generate implementations for them. You'll have to do that yourself at runtime. Fortunately, if you know what you're doing, that's not too hard.
    >>
    >> You'll have to override +resolveInstanceMethod: to figure out what the user is trying to do: get or set. You'll use the selector passed in to derive the key under which you're storing the data in the dictionary. With this information, you construct a block of the appropriate signature, capturing whatever information you need, and turn it into an IMP using imp_implementationWithBlock(). You can then add that method to the class using class_addMethod().
    >>
    >> So yes, this is totally possible. However, it seems like it'll be a lot of work if all you're doing is trying to save yourself from implementing a few setters and getters.
    >
    > Sorry, I misread. Since the OP doesn't care about pre-declaring all the properties, then the most elegant answer is, "use Core Data." Why reinvent the wheel? All the functionality is already there, and then some.
    >
    > OP: If for whatever reason you feel that you need to reinvent the wheel, or you need your own backing store that isn't CD-friendly, it seems much simpler to me to just override the "undefinedKey" methods.
    >
    > Keary Suska
    > Esoteritech, Inc.
    > "Demystifying technology for your home or business"
    >
    >> On 2012-07-11, at 6:46 AM, Keary Suska <cocoa-dev...> wrote:
    >>
    >>> On Jul 11, 2012, at 6:34 AM, Motti Shneor wrote:
    >>>
    >>>> Hi. I guess many have already stumped into this one, but my wildest phrasing attempts in web-searches didn't yield.
    >>>>
    >>>> I'd like to have a "data" object, similar to (but much simpler than) CoreData entity, which only exports a bunch of properties. Something like
    >>>>
    >>>> @interface MSPersonalData :NSObject
    >>>> {
    >>>> }
    >>>> -(id) initWithData:(NSDictionary*)initialData;
    >>>> -(id) init;
    >>>> @property (nonatomic, retain) NSString* firstName;
    >>>> @property (nonatomic, retain) NSString* lastName;
    >>>> @property (nonatomic, retain) NSDate*  birthDate;
    >>>> @property (nonatomic, retain) NSNumber* salary;
    >>>> @end
    >>>>
    >>>> I'd like this object to have NO ivars, and NO property implementations, not even synthesized. Instead I'd like the object to either inherit from NSMutableDictionary or own a single NSMutableDictionary ivar, and I'd like any access to my object to be transparently translated to internal dictionary valueForKey: and setValue:forKey:.
    >>>>
    >>>> Simply put, I'd like code like this:
    >>>>
    >>>> MSPersonalData *myDataObject = ;[MSPersonalData alloc] init];
    >>>> myDataObject.firstName = @"Motti";
    >>>>
    >>>> to actually perform
    >>>>
    >>>> [self  setValue:@"Motti" forKey: @"firstName"];    // if MSPersonalData inherits NSMutableDictionary
    >>>> or
    >>>> [internalDictionary  setValue:@"Motti" forKey: @"firstName"]; // if MSPersonalData owns  NSMutableDictionary.
    >>>>
    >>>> Such object can be very useful for UI binding,  and as self-documented API for a sub-system. Instead of declaring a set of static keys as string, just behave as if there were iVars per each key of the object. I think CoreData NSManagedObjects do this, in their "primitive" implementation.
    >>>>
    >>>> I tried several schemes for implementation, but all became cumbersome, and I had to do much trickery, and in the end it looked ugly. I strongly feel there must be a simple and elegant way to do this. To have a mutable dictionary react to the "syntactic sugar" of dot notation.
    >>>>
    >>>> Is there a good, general solution for this?
    >>>
    >>>
    >>> What is hampering you, it seems to me, is your requirement to use dot syntax. At least, to use dot syntax and not get a bunch of compiler warnings. Even Core Data still requires you to declare properties to avoid compiler warnings, which is a good sign that what you are after is not "cleanly" possible.
    >

    Motti Shneor,
    ---------------------------------------------------------
    But they are useless ---
            they can only provide answers!

    (Pablo Picasso 1881-1973 about Computers).
  • OP = original poster (i.e the person who started the thread)

    On Jul 11, 2012, at 4:45 PM, Motti Shneor <sumac...> wrote:

    > Wow, and thanks, everyone. BTW, what does "OP" stand for? (obviously, its me...)
  • On Jul 11, 2012, at 1:45 PM, Motti Shneor wrote:

    > Wow, and thanks, everyone. BTW, what does "OP" stand for? (obviously, its me...)

    OP = original poster

    > Of what I read from everyone, and after examining the suggested code from Jens Alfke, I think I'm inclined to something simpler, hinted by  Keary Suska.  Could you spare a few more words on the "undefinedKey" override?

    This is part of the NSKeyValueCoding protocol http://developer.apple.com/library/ios/#DOCUMENTATION/Cocoa/Reference/Found
    ation/Protocols/NSKeyValueCoding_Protocol/Reference/Reference.html
    .

    > I'd like to avoid dynamic auto-generation of methods at runtime. I can't see why this is needed --- ALL my getters and setters will look exactly the same.

    Actually, generation of methods is exactly what you want, because all of your getters and setters will look exactly the same.  The question arises as to how they should be generated.

    1.  Copy-Paste: bad idea. lots of code, really slow, easy to mess up, etc
    2.  At Runtime: arguably the most flexible approach, but also the most complex.
    3.  At compiletime: easy to understand.  Define a macro that you would use instead of "@synthesize" or "@dynamic".  Something like this:

    #define SYNTHESIZE(_property, _setter) \
    - (void) _setter (id)newValue { \
    [internalMutableDict setObject:newValue forKey:@"" ##_property]; \
    } \
    - (id) _property  { \
    return [internalMutableDict objectForKey:@"" ##_property]; \
    }

    And then you'd do:

    @implementation Foo

    SYNTHESIZE(firstName, setFirstName:)
    SYNTHESIZE(lastName, setLastName:)
    SYNTHESIZE(age, setAge:)

    @end

    And so on.  (Warning, typed in a Mail window and not tested)

    Dave

    >
    > We have dozens such "data classes", and I hate to copy-paste zillions of exactly-the-same implementations by hand.  For every property (e.g. "firstName") I would need to to write
    >
    > - (NSString *)firstName {
    > return [internalMutableDict valueForKey:firstName];
    > }
    > - (void) setFirstName :(NSString *)firstName {
    > [internalMutableDict setValue:firstName forKey:@"firstName"];
    > }
    >
    > Isn't writing 1000 such implementations error-prone? call for automation of some kind?
    >
    > The reason we insist on dot-notation for accessing these objects, is that the code which does this, is written in C++ or C style, using structs and dot notation. That code could compile with no change against C++ data objects (in Windows) or against my Propery-based data objects for Mac.
    >
    > So... It's not that we're anti-CD, on the contrary.... only this is a big project with lot's of legacy shared code imposed on the system, to say nothing of legacy thinking  and "shared thoughts" by management.
  • On Jul 11, 2012, at 1:45 PM, Motti Shneor <sumac...> wrote:

    > I'd like to avoid dynamic auto-generation of methods at runtime. I can't see why this is needed --- ALL my getters and setters will look exactly the same.

    It isn't strictly _needed_, but it's the only alternative to creating all the methods at compile time using either copy-paste or a macro like the one Dave DeLong suggested. If you want to access all these properties using native property syntax, instead of "-valueForKey:", then there has to exist a method for each of those properties. They can be created in the source code, or on the fly at runtime (which is the way Core Data does it.)

    —Jens
  • On Jul 11, 2012, at 2:45 PM, Motti Shneor wrote:
    > Of what I read from everyone, and after examining the suggested code from Jens Alfke, I think I'm inclined to something simpler, hinted by  Keary Suska.  Could you spare a few more words on the "undefinedKey" override?

    I would create a base class that implements -valueForUndefinedKey: and -setValue:forUndefinedKey: (per the doc links that another poster provided). These methods would simply get/set from the dictionary. The only thing that requires a little consideration is how you may choose to validate the key (if at all)--i.e. determine whether you want to throw an exception for unknown keys. You can throw by simply calling super's implementation. To validate you could keep an array of valid key names (somewhat fragile, as you need to keep it updated), or use runtime inspection functions (see: https://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/Obj
    CRuntimeGuide/Articles/ocrtPropertyIntrospection.html#//apple_ref/doc/uid/T
    P40008048-CH101-SW24
    ). More cryptic but not fragile.

    > Obviously, I would use CoreData if I could. In fact, we use CoreData for the base model of our application. However, there is another "layer" higher than the CoreData model, that consists of intermediate data objects that serve as mediators between server messages and our "real" model objects in CoreData. The "Keys" or property-names are dictated by server definitions, and do not fully match those we have in our CoreData model.
    >
    > We don't want these objects to be stored, and we don't want them to be dependent on some managedObjectContext (for threading reasons). Just simple, old-style, model objects.

    I actually have a large project that uses pars of Core Data without an NSManagedObjectContext, as I have to use a specific RDBMS back-end. In fact, the context isn't needed unless you want storage and/or synchronization (relationship management, etc). I use a Managed Object Model to represent the data schema so I can have introspection, and subclass NSManagedObject. I also use the undefined-key technique for them, using the associated NSEntityDescription for key validation. I also have a system that allows properties to be added dynamically from SQL query results. Works like a charm.

    > I'd like to avoid dynamic auto-generation of methods at runtime. I can't see why this is needed --- ALL my getters and setters will look exactly the same.

    As was mentioned, you at least have to specify @dynamic, which really only promises the compiler that valid getter/setters will be available at runtime.

    > The reason we insist on dot-notation for accessing these objects, is that the code which does this, is written in C++ or C style, using structs and dot notation. That code could compile with no change against C++ data objects (in Windows) or against my Propery-based data objects for Mac.

    This is all fine--just means that you have to create the classes and declare the properties to make the compiler happy.

    HTH,

    Keary Suska
    Esoteritech, Inc.
    "Demystifying technology for your home or business"
  • On Jul 11, 2012, at 4:54 PM, Keary Suska wrote:

    > On Jul 11, 2012, at 2:45 PM, Motti Shneor wrote:
    >> Of what I read from everyone, and after examining the suggested code from Jens Alfke, I think I'm inclined to something simpler, hinted by  Keary Suska.  Could you spare a few more words on the "undefinedKey" override?
    >
    > I would create a base class that implements -valueForUndefinedKey: and -setValue:forUndefinedKey: (per the doc links that another poster provided). These methods would simply get/set from the dictionary. The only thing that requires a little consideration is how you may choose to validate the key (if at all)--i.e. determine whether you want to throw an exception for unknown keys. You can throw by simply calling super's implementation. To validate you could keep an array of valid key names (somewhat fragile, as you need to keep it updated), or use runtime inspection functions (see: https://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/Obj
    CRuntimeGuide/Articles/ocrtPropertyIntrospection.html#//apple_ref/doc/uid/T
    P40008048-CH101-SW24
    ). More cryptic but not fragile.

    I'm probably missing something, but why bother with the undefinedKey methods?  Why not just override -setValue:forKey: and -valueForKey:?

    Dave
  • On Jul 11, 2012, at 6:53 PM, Dave DeLong wrote:

    >
    > On Jul 11, 2012, at 4:54 PM, Keary Suska wrote:
    >
    >> On Jul 11, 2012, at 2:45 PM, Motti Shneor wrote:
    >>> Of what I read from everyone, and after examining the suggested code from Jens Alfke, I think I'm inclined to something simpler, hinted by  Keary Suska.  Could you spare a few more words on the "undefinedKey" override?
    >>
    >> I would create a base class that implements -valueForUndefinedKey: and -setValue:forUndefinedKey: (per the doc links that another poster provided). These methods would simply get/set from the dictionary. The only thing that requires a little consideration is how you may choose to validate the key (if at all)--i.e. determine whether you want to throw an exception for unknown keys. You can throw by simply calling super's implementation. To validate you could keep an array of valid key names (somewhat fragile, as you need to keep it updated), or use runtime inspection functions (see: https://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/Obj
    CRuntimeGuide/Articles/ocrtPropertyIntrospection.html#//apple_ref/doc/uid/T
    P40008048-CH101-SW24
    ). More cryptic but not fragile.
    >
    > I'm probably missing something, but why bother with the undefinedKey methods?  Why not just override -setValue:forKey: and -valueForKey:?

    Good question--for me, it is a matter of extensibility. This way I can specifically handle pseudo-proeprties separate from implemented properties and not have to code for both. A good example would be dynamic or dependent properties that need custom methods. Also, since valueForKey methods are highly optimized, the extra overhead is negligible (in my tests, at least).

    HTH,

    Keary Suska
    Esoteritech, Inc.
    "Demystifying technology for your home or business"
  • Thanks again Keary.

    On 12 ביול 2012, at 02:54, Keary Suska wrote:

    > On Jul 11, 2012, at 2:45 PM, Motti Shneor wrote:
    >> Of what I read from everyone, and after examining the suggested code from Jens Alfke, I think I'm inclined to something simpler, hinted by  Keary Suska.  Could you spare a few more words on the "undefinedKey" override?
    >
    > I would create a base class that implements -valueForUndefinedKey: and -setValue:forUndefinedKey: (per the doc links that another poster provided). These methods would simply get/set from the dictionary. The only thing that requires a little consideration is how you may choose to validate the key (if at all)--i.e. determine whether you want to throw an exception for unknown keys. You can throw by simply calling super's implementation. To validate you could keep an array of valid key names (somewhat fragile, as you need to keep it updated), or use runtime inspection functions (see: https://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/Obj
    CRuntimeGuide/Articles/ocrtPropertyIntrospection.html#//apple_ref/doc/uid/T
    P40008048-CH101-SW24
    ). More cryptic but not fragile.
    >

    Thanks, I think this is exactly what I was looking for.

    In my case it is almost trivial. These classes maintain dictionaries to convert from the class' Keys (properties) to the base-model (CoreData) attribute names in the schema. I can simply look up the internal key, and throw (via super implementation of valueForUndefinedKey/setValue:forUndefinedKey) if I can't find it.

    If I'm going this route, do I still need to use the @dynamic for my properties? after all --- promised implementations wont be available in runtime. I rely on a KVO pre-exception mechanism.

    >> Obviously, I would use CoreData if I could. In fact, we use CoreData for the base model of our application. However, there is another "layer" higher than the CoreData model, that consists of intermediate data objects that serve as mediators between server messages and our "real" model objects in CoreData. The "Keys" or property-names are dictated by server definitions, and do not fully match those we have in our CoreData model.
    >>
    >> We don't want these objects to be stored, and we don't want them to be dependent on some managedObjectContext (for threading reasons). Just simple, old-style, model objects.
    >
    > I actually have a large project that uses pars of Core Data without an NSManagedObjectContext, as I have to use a specific RDBMS back-end. In fact, the context isn't needed unless you want storage and/or synchronization (relationship management, etc). I use a Managed Object Model to represent the data schema so I can have introspection, and subclass NSManagedObject. I also use the undefined-key technique for them, using the associated NSEntityDescription for key validation. I also have a system that allows properties to be added dynamically from SQL query results. Works like a charm.

    Can I come work with you? :) I like it. Sounds like a healthy little system...

    Do you use the graphic tool for defining the Managed Object Model, or do you do this programmatically?
    If I choose this way, I'd like still to be able to auto-generate the classes from the schema. Our "cross-platform guys" would not rest until they see a concrete source file declaring a C++ style class with their own eyes.
    Also, when you have a "partial-use" of CoreData (a NSManagedObject subclass that lives without a context) How do you instantiate it? via alloc init? CoreData API demands a context...
    + (id)insertNewObjectForEntityForName:(NSString *)entityName inManagedObjectContext:(NSManagedObjectContext *)context;

    >> I'd like to avoid dynamic auto-generation of methods at runtime. I can't see why this is needed --- ALL my getters and setters will look exactly the same.
    >
    > As was mentioned, you at least have to specify @dynamic, which really only promises the compiler that valid getter/setters will be available at runtime.

    This is alright with me. @dynamic we'll write :)

    >
    >> The reason we insist on dot-notation for accessing these objects, is that the code which does this, is written in C++ or C style, using structs and dot notation. That code could compile with no change against C++ data objects (in Windows) or against my Propery-based data objects for Mac.
    >
    > This is all fine--just means that you have to create the classes and declare the properties to make the compiler happy.

    We do it anyway today.... There will be no change to the header files. I only wish to delete current implementation, in favor of compact, generic code in a super class.
    >
    > HTH,

    This HELPS indeed. Thanks.

    Motti Shneor
    ---------------------------------------------------------
    Ceterum censeo Microsoftinem delendam esse
  • On Jul 12, 2012, at 00:14 , Motti Shneor wrote:

    > On 12 ביול 2012, at 02:54, Keary Suska wrote:
    >
    >> On Jul 11, 2012, at 2:45 PM, Motti Shneor wrote:
    >>> Of what I read from everyone, and after examining the suggested code from Jens Alfke, I think I'm inclined to something simpler, hinted by  Keary Suska.  Could you spare a few more words on the "undefinedKey" override?
    >>
    >> I would create a base class that implements -valueForUndefinedKey: and -setValue:forUndefinedKey: (per the doc links that another poster provided). These methods would simply get/set from the dictionary. The only thing that requires a little consideration is how you may choose to validate the key (if at all)--i.e. determine whether you want to throw an exception for unknown keys. You can throw by simply calling super's implementation. To validate you could keep an array of valid key names (somewhat fragile, as you need to keep it updated), or use runtime inspection functions (see:https://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/Obj
    CRuntimeGuide/Articles/ocrtPropertyIntrospection.html#//apple_ref/doc/uid/T
    P40008048-CH101-SW24
    ). More cryptic but not fragile.
    >>
    >
    > Thanks, I think this is exactly what I was looking for.
    >
    > In my case it is almost trivial. These classes maintain dictionaries to convert from the class' Keys (properties) to the base-model (CoreData) attribute names in the schema. I can simply look up the internal key, and throw (via super implementation of valueForUndefinedKey/setValue:forUndefinedKey) if I can't find it.
    >
    > If I'm going this route, do I still need to use the @dynamic for my properties? after all --- promised implementations wont be available in runtime. I rely on a KVO pre-exception mechanism.

    Maybe I haven't been following this closely enough, but you and Keary seem to be talking at cross purposes.

    -- If you *do* want to call property accessors in clients of your class, then the KVC method such as 'valueForUndefinedKey:' are of no use to you, because the accessors don't go through KVC. (It's the other way round -- KVC will call the accessors if they exist.)

    In this case, I think you should take a look at 'forwardInvocation:', documented here:

    https://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/Obj
    CRuntimeGuide/Articles/ocrtForwarding.html#//apple_ref/doc/uid/TP40008048-C
    H105-SW1


    Note that you can't actually forward the invocation as is, since there's no implementation of the relevant methods anywhere, but notice the comment that "A forwardInvocation: method can act as a distribution center for unrecognized messages, … . It can translate one message into another, …". In other words, you'll have to detect/validate the selector/parameter pattern in the invocation, then modify or recreate the invocation to refer to a pair of methods that do the actual work. None of this should be very hard, since there are only 2 selector patterns to detect.

    This technique would avoid mucking about with the runtime class information directly (which you seem reluctant to do).

    The only issues I see are:

    a. I think you'll need to override 'respondsToSelector:' as well as 'forwardInvocation:', so that KVC will think that your dynamic accessors exist.

    b. This all depends on knowing whether property names are valid, but you say that you know that without having to add any data structures to your class implementation. You'll just have to define the properties as '@dynamic' to keep the compiler from complaining.

    If you're not willing (or able, for some reason I haven't foreseen) to use an approach like this, I think Dave's suggestion of macros is your best solution, although it's a little bit ugly at the source code level.

    -- If you *don't* want to call property accessors in clients of your class, but use the class only for (e.g.) bindings or other things that use KVC, then 'valueForUndefinedKey:' etc. is the right way to go.
  • Thanks Quincey. I seem to have missed that one.

    I already have dealt with forwardInvocation, in another class (some kind of NSNotificationCenter replacement which dispatches messages to registered objects, in prioritized and synchronized way). I think I can do it without much hassle. This covers things like [myObject firstName] in addition to myObject.firstName, and [myObject setFirstName] in addition to myObject.firstName = @"xxx";

    I do not intend to forward the invocation at all, rather to parse it, and call my internal general-purpose setter/getter method.

    I completely forgot about "respondsToSelecor:" but here, again, it can be done --- only I hate to implement cocoa's naming convention myself (knowing I have a key like "firstName", to respond to a @selector(setFirstName:). Isn't the machinery for that part of the runtime? Who implements those naming conventions?

    On 12 ביול 2012, at 10:59, Quincey Morris wrote:

    > On Jul 12, 2012, at 00:14 , Motti Shneor wrote:
    >
    >> On 12 ביול 2012, at 02:54, Keary Suska wrote:
    >>
    >>> On Jul 11, 2012, at 2:45 PM, Motti Shneor wrote:
    >>>> Of what I read from everyone, and after examining the suggested code from Jens Alfke, I think I'm inclined to something simpler, hinted by  Keary Suska.  Could you spare a few more words on the "undefinedKey" override?
    >>>
    >>> I would create a base class that implements -valueForUndefinedKey: and -setValue:forUndefinedKey: (per the doc links that another poster provided). These methods would simply get/set from the dictionary. The only thing that requires a little consideration is how you may choose to validate the key (if at all)--i.e. determine whether you want to throw an exception for unknown keys. You can throw by simply calling super's implementation. To validate you could keep an array of valid key names (somewhat fragile, as you need to keep it updated), or use runtime inspection functions (see:https://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/Obj
    CRuntimeGuide/Articles/ocrtPropertyIntrospection.html#//apple_ref/doc/uid/T
    P40008048-CH101-SW24
    ). More cryptic but not fragile.
    >>>
    >>
    >> Thanks, I think this is exactly what I was looking for.
    >>
    >> In my case it is almost trivial. These classes maintain dictionaries to convert from the class' Keys (properties) to the base-model (CoreData) attribute names in the schema. I can simply look up the internal key, and throw (via super implementation of valueForUndefinedKey/setValue:forUndefinedKey) if I can't find it.
    >>
    >> If I'm going this route, do I still need to use the @dynamic for my properties? after all --- promised implementations wont be available in runtime. I rely on a KVO pre-exception mechanism.
    >
    > Maybe I haven't been following this closely enough, but you and Keary seem to be talking at cross purposes.
    >
    > -- If you *do* want to call property accessors in clients of your class, then the KVC method such as 'valueForUndefinedKey:' are of no use to you, because the accessors don't go through KVC. (It's the other way round -- KVC will call the accessors if they exist.)
    >
    > In this case, I think you should take a look at 'forwardInvocation:', documented here:
    >
    > https://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/Obj
    CRuntimeGuide/Articles/ocrtForwarding.html#//apple_ref/doc/uid/TP40008048-C
    H105-SW1

    >
    > Note that you can't actually forward the invocation as is, since there's no implementation of the relevant methods anywhere, but notice the comment that "A forwardInvocation: method can act as a distribution center for unrecognized messages, … . It can translate one message into another, …". In other words, you'll have to detect/validate the selector/parameter pattern in the invocation, then modify or recreate the invocation to refer to a pair of methods that do the actual work. None of this should be very hard, since there are only 2 selector patterns to detect.
    >
    > This technique would avoid mucking about with the runtime class information directly (which you seem reluctant to do).
    >
    > The only issues I see are:
    >
    > a. I think you'll need to override 'respondsToSelector:' as well as 'forwardInvocation:', so that KVC will think that your dynamic accessors exist.
    >
    > b. This all depends on knowing whether property names are valid, but you say that you know that without having to add any data structures to your class implementation. You'll just have to define the properties as '@dynamic' to keep the compiler from complaining.
    >
    > If you're not willing (or able, for some reason I haven't foreseen) to use an approach like this, I think Dave's suggestion of macros is your best solution, although it's a little bit ugly at the source code level.
    >
    > -- If you *don't* want to call property accessors in clients of your class, but use the class only for (e.g.) bindings or other things that use KVC, then 'valueForUndefinedKey:' etc. is the right way to go.
    >
    >

    Motti Shneor
    e-mail: <motti.shneor...>
    phone: +972-8-9267730
    mobile: +972-54-3136621
    ----------------------------------------
    Ceterum censeo Microsoftinem delendam esse
  • On Jul 12, 2012, at 3:32 AM, Motti Shneor wrote:

    > Thanks Quincey. I seem to have missed that one.

    Yes, Quincy is right. I was forgetting about the dot syntax issue as I haven't been using it much for my implementation. Note, however, that if you plan to use bindings with these objects you will have to also implement the kind of approach we discussed.

    > I already have dealt with forwardInvocation, in another class (some kind of NSNotificationCenter replacement which dispatches messages to registered objects, in prioritized and synchronized way). I think I can do it without much hassle. This covers things like [myObject firstName] in addition to myObject.firstName, and [myObject setFirstName] in addition to myObject.firstName = @"xxx";

    The bit I forgot is that dot syntax is really syntactic sugar--all the compiler does with it is compile the statement as if it was actually written using a method call. I.e., [myObject firstName] is exactly the same operation as myObject.firstName.

    > I do not intend to forward the invocation at all, rather to parse it, and call my internal general-purpose setter/getter method.
    >
    > I completely forgot about "respondsToSelecor:" but here, again, it can be done --- only I hate to implement cocoa's naming convention myself (knowing I have a key like "firstName", to respond to a @selector(setFirstName:). Isn't the machinery for that part of the runtime? Who implements those naming conventions?

    You can handle the invocation any way you want, including ignoring it entirely. You shouldn't need to override respondsToSelector: as Quincy pondered, if you also implement the undefined key approach. If you don't, then you might have to.

    The forwardInvocation implementation would be easy. In theory, all you need to distinguish is the setX vs X pattern, determine the actual key name (really just extracting and formatting when you get a setX method), change the selector of the invocation to the appropriate undefined key method, and invoke it.

    HTH,

    Keary Suska
    Esoteritech, Inc.
    "Demystifying technology for your home or business"

    > On 12 ביול 2012, at 10:59, Quincey Morris wrote:
    >
    >> On Jul 12, 2012, at 00:14 , Motti Shneor wrote:
    >>
    >>> On 12 ביול 2012, at 02:54, Keary Suska wrote:
    >>>
    >>>> On Jul 11, 2012, at 2:45 PM, Motti Shneor wrote:
    >>>>> Of what I read from everyone, and after examining the suggested code from Jens Alfke, I think I'm inclined to something simpler, hinted by  Keary Suska.  Could you spare a few more words on the "undefinedKey" override?
    >>>>
    >>>> I would create a base class that implements -valueForUndefinedKey: and -setValue:forUndefinedKey: (per the doc links that another poster provided). These methods would simply get/set from the dictionary. The only thing that requires a little consideration is how you may choose to validate the key (if at all)--i.e. determine whether you want to throw an exception for unknown keys. You can throw by simply calling super's implementation. To validate you could keep an array of valid key names (somewhat fragile, as you need to keep it updated), or use runtime inspection functions (see:https://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/Obj
    CRuntimeGuide/Articles/ocrtPropertyIntrospection.html#//apple_ref/doc/uid/T
    P40008048-CH101-SW24
    ). More cryptic but not fragile.
    >>>>
    >>>
    >>> Thanks, I think this is exactly what I was looking for.
    >>>
    >>> In my case it is almost trivial. These classes maintain dictionaries to convert from the class' Keys (properties) to the base-model (CoreData) attribute names in the schema. I can simply look up the internal key, and throw (via super implementation of valueForUndefinedKey/setValue:forUndefinedKey) if I can't find it.
    >>>
    >>> If I'm going this route, do I still need to use the @dynamic for my properties? after all --- promised implementations wont be available in runtime. I rely on a KVO pre-exception mechanism.
    >>
    >> Maybe I haven't been following this closely enough, but you and Keary seem to be talking at cross purposes.
    >>
    >> -- If you *do* want to call property accessors in clients of your class, then the KVC method such as 'valueForUndefinedKey:' are of no use to you, because the accessors don't go through KVC. (It's the other way round -- KVC will call the accessors if they exist.)
    >>
    >> In this case, I think you should take a look at 'forwardInvocation:', documented here:
    >>
    >> https://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/Obj
    CRuntimeGuide/Articles/ocrtForwarding.html#//apple_ref/doc/uid/TP40008048-C
    H105-SW1

    >>
    >> Note that you can't actually forward the invocation as is, since there's no implementation of the relevant methods anywhere, but notice the comment that "A forwardInvocation: method can act as a distribution center for unrecognized messages, … . It can translate one message into another, …". In other words, you'll have to detect/validate the selector/parameter pattern in the invocation, then modify or recreate the invocation to refer to a pair of methods that do the actual work. None of this should be very hard, since there are only 2 selector patterns to detect.
    >>
    >> This technique would avoid mucking about with the runtime class information directly (which you seem reluctant to do).
    >>
    >> The only issues I see are:
    >>
    >> a. I think you'll need to override 'respondsToSelector:' as well as 'forwardInvocation:', so that KVC will think that your dynamic accessors exist.
    >>
    >> b. This all depends on knowing whether property names are valid, but you say that you know that without having to add any data structures to your class implementation. You'll just have to define the properties as '@dynamic' to keep the compiler from complaining.
    >>
    >> If you're not willing (or able, for some reason I haven't foreseen) to use an approach like this, I think Dave's suggestion of macros is your best solution, although it's a little bit ugly at the source code level.
    >>
    >> -- If you *don't* want to call property accessors in clients of your class, but use the class only for (e.g.) bindings or other things that use KVC, then 'valueForUndefinedKey:' etc. is the right way to go.
  • Sent from Jane

    On 2012-07-12, at 7:05 AM, Keary Suska <cocoa-dev...> wrote:

    > The forwardInvocation implementation would be easy.

    It would also be slow.

    > In theory, all you need to distinguish is the setX vs X pattern, determine the actual key name (really just extracting and formatting when you get a setX method), change the selector of the invocation to the appropriate undefined key method, and invoke it.

    If you're going to go to all that trouble to compute the key name, then you should go with dynamic method resolution instead. It'd only be a little bit more work at that point, would be much more flexible, and a lot faster.

    Dave
  • On Jul 12, 2012, at 2:32 AM, Motti Shneor <sumac...> wrote:

    > I already have dealt with forwardInvocation, in another class (some kind of NSNotificationCenter replacement which dispatches messages to registered objects, in prioritized and synchronized way). I think I can do it without much hassle.

    -forwardInvocation: is REALLY slow. It has to try every other way to find the method, then look up the method signature, extract the parameters from the stack frame, create an NSInvocation object, copy all the parameters into it, then call -forwardInvocation:.

    By comparison, creating the methods at runtime is comparably slow on the first call, then much faster after that. It's still going to be slower than a regular method call because the implementation has to figure out which method you were calling and map that to a property name, but it's not as bad.

    —Jens
  • On Thu, Jul 12, 2012, at 02:40 PM, Jens Alfke wrote:
    > By comparison, creating the methods at runtime is comparably slow on the
    > first call, then much faster after that. It's still going to be slower
    > than a regular method call because the implementation has to figure out
    > which method you were calling and map that to a property name, but it's
    > not as bad.

    Not necessarily. Now that we have blocks-as-IMPs, you could choose to
    create a separate IMP for each property you're dynamically adding,
    rather than disassembling the selector from within the IMP.

    Classic time/memory tradeoff.

    --Kyle Sluder
  • On Jul 12, 2012, at 2:44 PM, Kyle Sluder <kyle...> wrote:

    > Not necessarily. Now that we have blocks-as-IMPs

    *scratching-record sound*

    We do?! How do I get in on that goodness?

    —Jens
  • On Thu, Jul 12, 2012, at 02:49 PM, Jens Alfke wrote:
    >
    > On Jul 12, 2012, at 2:44 PM, Kyle Sluder <kyle...> wrote:
    >
    >> Not necessarily. Now that we have blocks-as-IMPs
    >
    > *scratching-record sound*
    >
    > We do?! How do I get in on that goodness?

    Use imp_implementationWithBlock (available as of OS X 10.7 and iOS 4.3):
    http://opensource.apple.com/source/objc4/objc4-493.11/runtime/runtime.h

    More info here:
    http://www.friday.com/bbum/2011/03/17/ios-4-3-imp_implementationwithblock/

    --Kyle Sluder
previous month july 2012 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