views:

35

answers:

1

I have a series of models for my application. Across all these models there are (will be) some 200 or 300 instance variables. The application stores its persistent data on a web-based server (MySQL - but I guess that part doesn't matter). Whenever a model iVar is updated I need to make a call to the server to update the appropriate value for that iVar.

My current model strategy is (header file):

@interface MyModel : NSObject {

    NSString * firstName;
    NSString * lastName;
}

@property (readwrite, copy) NSString * firstName;
@property (readwrite, copy) NSString * lastName;

@end

(implementation file):

@implementation MyModel

@synthesize firstName;
@synthesize lastName;

-(id)init {

    [super init]

    [self setFirstName:@"George"];
    [self setLastName:@"Kastanza"];

    return self;
}

-(void)setFirstName:(NSString *)aName {

    // call method to update server with new value here
    firstName = aName;
}

-(void)setLastName:(NSString *)aName {

    // call method to update server with new value here
    lastName = aName;
}

@end

The problem is that if I have 200 or 300 iVar's all needing to go through the same update call to the server that means writing a lot of setters. Moreover, if I need to make a change to the method call, I'd have to update each and every method in every setter i the entire application.

Is there a process by which I could run every set of an iVar through a method first, before setting?

I thought of having just a NSMutableDictionary per model object to store all of the iVar's, but that abstracts the setters and getters and may introduce a big memory footprint for so many dictionaries. However, doing it this way means that every time the dictionary is set I could pass it through one method.

As I understand it dynamically adding iVar's at runtime to an object model is considered a bad thing because of the pointer referencing for any subclasses that may be dependent upon the model (the subclass pointer doesn't get offset unless a complete recompile is done).

Any ideas and suggestions much appreciated.

Update

Based upon Ole's recommendation here is the solution (although it uses a little more code than a few lines unfortunately)...

In the model I added a method that I can set when I need to. I didn't call the method directly from the init, because adding a whole bunch of results returned from the server would trigger the observers for every object added. So I call the method after I have initialized and updated the first grab from the server.

Here's the code...

-(void)registerObservers {

    [self addObserver:self 
           forKeyPath:@"firstName"
              options:NSKeyValueObservingOptionNew 
              context:NULL];

    [self addObserver:self 
           forKeyPath:@"lastName" 
              options:NSKeyValueObservingOptionNew 
              context:NULL];
}

Then I add the observer to the model:

-(void)observeValueForKeyPath:(NSString *)keyPath
                 ofObject:(id)object
                   change:(NSDictionary *)change
                  context:(void *)context {

    if ([keyPath isEqual:@"firstName"]) {
        // Do whatever I need to do
    }

    if ([keyPath isEqual:@"lastName"]) {
        // Do whatever I need to do
    }
}

In my real implementation I also happen to post a notification of the object set to self so that I can update anything that should be listening but isn't paying attention (like stuff in NSArrayControllers).

A: 

Use Key-Value Observing. You have to manually register yourself as an observer for every property, though.

Ole Begemann
@Ole - thanks for the feedback. I'm not sure that really buys me anything because then I'd be writing an observer for every iVar instead of a couple lines more code for the setter. What I was hoping was figuring a way of doing something like subclassing @synthesize so that every setter would go through 1 custom method on my object model before being handed off to the setter itself.
Hooligancat
No, you'd be writing 400 lines of boilerplate code (200 to register yourself as observer and 200 to remove yourself as an observer) but the actual method that's called when a value changes is always the same. And by using the Obj-C runtime to enumerate all of your class's properties, you can possibly also reduce the boilerplate code to a loop (see `class_copyPropertyList`, `property_getName` and `property_getAttributes`).
Ole Begemann
@Ole - OK - now I like the approach! :-) Enumerating through the objects to get the class properties might be a slick answer to my prayers. Thanks!!
Hooligancat
Well.. that coupled with the observer's of course...
Hooligancat
Well that worked out perfectly. I will post an update shortly so that others can see a working option...
Hooligancat