Monitoring Property Changes On Objects

Today we go over the various ways that you can monitor properties in your classes for changes, and act on those changes. There are three ways that I am going to demonstrate.

  • Manually - Notify a specific class of changes to specific properties manually.
  • Monitoring statically via KVO - Notifies specific classes of changes made to specific properties automatically.
  • Monitoring dynamically via KVO - Notifies any class of changes made to any property, without having to specify which properties by using the Objective-C runtime library. Yay C functions!

Object monitoring

There are instancs were monitoring property changes to an object can be very useful. You might want to monitor changes to various object properties that might need to get wrote to a local database, propogate back to a server, or perhaps you want to immediately write out property changes to file. Monitoring an object for changes allows you to save the changes as they happen, and prevent invoking any save methods or update methods when nothing has changed.

In order to monitor the properties of an object, you need to determine what the extent of the monitoring needs to be. Are you only going to monitor a handful of properties in a single object? Perhaps you have several dozen properties spread out over multiple objects, or you might want to make something flexible so you can re-use it on other projects if needed.

I should note that two of the three approachs I'm going to discuss use KVO. If you have a lot of objects and need your app to be fairly performant, this might not be the best solution for you. While KVO is very quick, it does come with some additional overhead. The performance hit is negligible on standard apps, or even hard-core apps with very few KVO's; a large quantity (we're talking hundreds) of KVO setups can significantly affect your performance though. A general rule of thumb is not to use these in any kind of app/game loop and keep the code that they perform fairly short. It should be used primarily as a callback, letting something know that things have changed.

Manually

Manually is the most straight forward approach. You create a property, preferably a BOOL, and set it to YES when a property is changed. In the BOOL properties setter, you would invoke what ever method that is neccessary to commit or process those changes.

It's best to demonstrate with an exampe, so let's say we have a class we are building called PlayerSettings and this class is responsible for saving the players settings in a game. When a setting is changed, we want to automatically save the settings to what ever format you happen to choose. The format isn't important, getting to the save code automatically without manually invoking a [player saveSettings]; method all the time is.

Lets start with the header API, which just exposes the public properties for the player settings class.

#import <Foundation/Foundation.h>

@interface MYPlayerSettings : NSObject

@property (strong, nonatomic) NSString *name;
@property (nonatomic) int age;
@property (nonatomic) double mouseSensitivity;
@property (strong, nonatomic) NSString *currentLevel;

@end

What we did was create four properties that pertain to the player settings. If any of these four properties are changed, we want to instantly save the player settings. Notice that there is no - (void)saveSettings; method? That's because there will be no need to manually save the settings; we'll do it all automatically. In order to do that though, we need to implement the player settings, so lets get to that.

#import "MYPlayerSettings.h"

@interface MYPlayerSettings ()
@property (nonatomic) BOOL settingsChanged;
@end

@implementation MYPlayerSettings
@end

In order to make things simple, we are going to use a BOOL property called settingsChanged. This property will be the only thing responsible for saving the player settings. I'll show you how by implementing the properties setter method next.

#import "MYPlayerSettings.h"

@interface MYPlayerSettings ()
@property (nonatomic) BOOL settingsChanged;
@end

@implementation MYPlayerSettings

// Setter method
- (void)setSettingsChanged:(BOOL)settingsChanged {
    _settingsChanged = settingsChanged;
    if (settingsChanged) [self savePlayerSettings];
    _settingsChanged = NO; // We are saved; no longer changed.
}

- (void)savePlayerSettings {
    // Do stuff.
}
@end

In our settingsChanged setter method, we check if the settingsChanged property is being set to YES. If it is, then we need to save the player settings, so we invoke the [self savePlayerSettings]; method. Note that the savePlayerSettings method is private; just like I said above, the player settings class will handle saving itself, there is no need to expose the method via the public API.

Next, we need to actually provide a way to set the settingsChanged property right? We do that by implementing the setter methods for our four properties like so:

- (void)setName:(NSString *)name {
    _name = name;
    self.settingsChanged = YES;
}

- (void)setAge:(int)age {
    _age = age;
    self.settingsChanged = YES;
}

- (void)setCurrentLevel:(NSString *)currentLevel {
    _currentLevel = currentLevel;
    self.settingsChanged = YES;
}

- (void)setMouseSensitivity:(double)mouseSensitivity {
    _mouseSensitivity = mouseSensitivity;
    self.settingsChanged = YES;
}

Not to bad right? Anytime that your game needs to adjust a setting, the player settings will automatically get saved. Now this does have some drawbacks, such as what happens if you change the player's settings from two different threads or what if you save method is asynchronous? There are some edge cases to consider with this approach, so keep that in mind. For small objects such as this, with quick file I/O operations, this would work just fine.

What if I have a handful of classes, each with a dozen properties that I want to implement this with? Isn't there an easier way? Why yes there is. You can either statically monitor or dynamically monitor an object via KVO, both of which I'll show you next.

Monitoring statically via KVO Part 1:

We will continue to use the PlayerSettings class for this example, but we will build on it because (hypothetically) you have added additional settings to the player that need to be saved. As mentioned above, it's a royal pain to write out a setter method for every single property, just for the sake of setting settingsChanged to YES. Let's do this a bit differently. We will use KVO.

KVO stands for Key-value observing and it provides a mechanism for objects to be notified when changes are made to a object's properties. There are a couple of ways we could do this, one of which modifies the above setter methods to look like such:

#import "MYPlayerSettings.h"

@implementation MYPlayerSettings

- (void)setName:(NSString *)name {
    _name = name;
    [[NSNotificationCenter defaultCenter] postNotificationName:@"settingChanged" object:self];
}

- (void)setAge:(int)age {
    _age = age;
    [[NSNotificationCenter defaultCenter] postNotificationName:@"settingChanged" object:self];
}

- (void)setCurrentLevel:(NSString *)currentLevel {
    _currentLevel = currentLevel;
    [[NSNotificationCenter defaultCenter] postNotificationName:@"settingChanged" object:self];
}

- (void)setMouseSensitivity:(double)mouseSensitivity {
    _mouseSensitivity = mouseSensitivity;
    [[NSNotificationCenter defaultCenter] postNotificationName:@"settingChanged" object:self];
}

- (void)savePlayerSettings {
    // Do stuff.
}
@end

As you can see, we no longer have the @property (nonatomic) BOOL settingsChanged; property, nor the setter method were we invoke the [self savePlayerSettings]; method. Instead, we have this nifty [[NSNotificationCenter defaultCenter] postNotificationName:object:]; call. What this does is send a message to the specified object (in this case self) with a notification called settingChanged. The notification name can be anything, I just happened to use settingChanged because it seems appropriately titled.

Once this happens, NSNotificationCenter will look for any object that is registered to receive the settingChanged message. This is called observing an object and in this case, the above code will do nothing because we have not added any observers. We will do that by implementing the PlayerSettings initializer method.

- (id)init {
    self = [super init];
    if (self) {
        [[NSNotificationCenter defaultCenter]
         addObserver:self
         selector:@selector(savePlayerSettings)
         name:@"settingChanged"
         object:self];
    }
    return self;
}

In the initializer we are adding ourself as on observe to any property within ourself that sends the settingChanged notification. It's important that the object argument has self as the parameter, otherwise our PlayerSettings class will be registering to receive settingChanged notifications from any object, which could be bad. For this example, we only want to receive messages from changes made to ourself. Also note that the parameter selector:@selector(savePlayerSettings) is telling NSNotificationCenter to invoke our [self savePlayerSettings] method when it receives the settingChanged notification. Now, unlike our manual approach, we are no longer manually invoking the save method in our private code either!

Now, let's assume someplace in your game you need to instance some player settings and assign some values. You would do so like this:

MYPlayerSettings *settings = [[MYPlayerSettings alloc] init];
settings.name = @"Bob";
settings.age = 22;
settings.currentLevel = @"Last level";

Each time you assign a property a value, the settings object will be saved automatically. As mentioned before, it works just fine for something small like this, or if you are only saving the changes. In the event that you have a massive amount of properties, or a lot of objects that will have it's entire object saved or transferred, you don't want to have your save code being called after each assignment. What if your save code is sending the data across the network to a server someplace? You are wasting data by just sending the save data across the network multiple times. This needs to be fixed! On top of that, wasn't the point of using KVO to elemenate the need for implementing the setter method on our properties? We're still doing that! Let's take care of that.

Monitoring statically via KVO Part 2:

We will continue to use our previous PlayerSettings.h file, but we are going to start fresh on our PlayerSettings.m file. It should look like this:

#import "MYPlayerSettings.h"

@implementation MYPlayerSettings
@end

Nice and empty! Now, as your class grows, it can be a pain in the behind to continously add new setter method for our properties, so that's going to stop. Instead, we will rely on KVO a bit differently by observing actual properties themselves rather than observing a object for a broadcasted message. Our PlayerSettings will no longer broadcast a settingChanged message when properties are changed, instead the PlayerSettings class will be self-aware and know when it has had it's own properties changed. In order to do this, we need to do a couple of things first. We need to implement a init method, a observeValueForKeyPath:ofObject:change:context: method and finally our savePlayerSettings method.

To get started, the PlayerSettings object needs to know what properties to monitor for changes, so we implement our initializer method and add ourself as an observer to those properties.

- (id)init {
    self = [super init];
    if (self) {
        [self addObserver:self forKeyPath:@"name" options:0 context:NULL];
        [self addObserver:self forKeyPath:@"age" options:0 context:NULL];
        [self addObserver:self forKeyPath:@"currentLevel" options:0 context:NULL];
        [self addObserver:self forKeyPath:@"mouseSensitivity" options:0 context:NULL];
    }
    return self;
}

That's a pretty easy initializer to implement, we just tell ourself that we want to observe ourself, monitoring each property that we specify. Now, when a property is changed, our PlayerSettings object will be made aware of it! How does this happen though? If you remember earlier, we specified that we wanted our savePlayerSettings method to be used right? In the above code, we aren't specifying any method, so what gets invoked? The answer to that lays in the Apple Documentation:

The observer must implement the key-value observing method observeValueForKeyPath:ofObject:change:context:.

Since we are observing ourself, we have to implement the observeValueForKeyPath:ofobject:change:context: method ourself. When a property is changed, this method will automatically be invoked for us. So that means, within that method, we can invoke our savePlayerSettings method.

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context
{
    [self savePlayerSettings];
}

- (void)savePlayerSettings {
    NSLog(@"%@ saved", self.name);
}

Now we have a fully automated save system and it's actually fewer lines of code than before. As you add new properties to the PlayerSettings class, you just need to go and observe them in the initializer.

We still have the same problem however of saving multiple times when we don't really need to. Take the following code, re-used from above:

MYPlayerSettings *settings = [[MYPlayerSettings alloc] init];
settings.name = @"Bob";
settings.age = 22;
settings.currentLevel = @"Last level";
settings.mouseSensitivity = 5.0;

Due to all four properties being changed, the save code will be called four different times. Is that efficient? Not really, so we need to fix that. I chose to take a similar approach as to what Apple did with their UI animations. We will add two new methods and two different properties. The methods will be called beginUpdates and endUpdates and our properties will be two BOOL values called settingsChanged and performingBatchChanges.

First let's create the two properties in our .m file like such:

@interface MYPlayerSettings ()
@property (nonatomic) BOOL settingChanged;
@property (nonatomic) BOOL performingBatchChanges;
@end

When we invoke our observeValueForKeyPath: method, we will need to perform a check. First, check if we are performingBatchChanges and if so, do not save the player settings. Since we are doing batch changes, we will save the player setting once we are completed with all of the changes. Since we know that settings have changed though, we need to set settingChanged to YES.

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context
{
    if (self.performingBatchChanges) {
        self.settingChanged = YES;
    } else {
        [self savePlayerSettings];
    }
}

In order to perform the batch changes, we need to implement our two new methods, beginUpdates and endUpdates. These two methods are really simple and need to be added to your .h file like this:

#import <Foundation/Foundation.h>

@interface MYPlayerSettings : NSObject

@property (strong, nonatomic) NSString *name;
@property (nonatomic) int age;
@property (nonatomic) double mouseSensitivity;
@property (strong, nonatomic) NSString *currentLevel;

- (void)beginUpdates;
- (void)endUpdates;
@end

Next we will add the implementation of these two new methods into our .m file like this:

- (void)beginUpdates {
    self.performingBatchChanges = YES;
}

- (void)endUpdates {
    self.performingBatchChanges = NO;

    if (self.settingChanged) {
        [self savePlayerSettings];
    }
}

This is pretty straight forward. When a property is changed, our observeValueForKeyPath: method is invoked. We check if we are performing batch changes (due to the user invoking beginUpdates prior to changing the property) and we set the settingChanged property to YES to indicate that we have changed settings but not saved them. If this is not a batch change, then we just save the settings. Once the user invokes the endUpdates method, we set our performingBatchChanges to NO because we are no longer making batch changes. Lastly, we have to check if the settings were changed and if they were, save them. Why perform this check? It's possible for the user to do the following:

[self.playerSettings beginUpdates];
[self.playerSettings endUpdates];

If they invoked the beginUpdates and endUpdates methods without ever making any changes, then we would be needlessly saving the player settings.

So, how do we use this? Pretty simply, if you are changing one or two properties, just use the settings file like normal.

self.playerSettings = [[MYPlayerSettings alloc] init];
self.playerSettings.mouseSensitivity = 5.0;

There's no harm in changing one or two settings, but if you are wanting to change several, then you use your new beginUpdates and endUpdates methods.

self.playerSettings = [[MYPlayerSettings alloc] init];
[self.playerSettings beginUpdates];
self.playerSettings.name = @"Bob";
self.playerSettings.age = 22;
self.playerSettings.currentLevel = @"Last level";
self.playerSettings.mouseSensitivity = 5.0;
[self.playerSettings endUpdates];

The player settings will only be saved once now, at the end when endUpdates is invoked. Saves on I/O or data usage if the content is sent over the network.

Alright, we are now monitoring our object's properties and automatically invoking our save method. What if we want to add another object, like a GameSettings to our project? It's simple enough that we can re-use the above code and get it up and running, but what if we could just write the code once and never re-write it again; while using across dozens of classes? Monitoring property changes dynamically via KVO can get the job done.

Monitoring dynamically via KVO

This approach digs into the Objective-C runtime and requires use of it's C runtime functions. I assume the reader has some understanding of what ARC does and is famiiar with introspection. As with most dynamic approaches, this one comes with the largest performance hit. You will not see the hit unless you are using this on hundreds of objects (like in a game).

Let's start off by creating a new class called MYGameSettings and providing it with some properties in the header.

#import <Foundation/Foundation.h>

@interface MYGameSettings : NSObject

@property (strong, nonatomic) NSString *version;
@property (nonatomic) int brightness;
@property (nonatomic) BOOL hardMode;

@end

We will save the implementation for last, as we need to build our dynamic object monitoring class first.

We are going to create a new class called ObjectMonitor. This object will be used to observe our classes from now on, monitoring what happens to the classes and then acting on the changes that take place. Let's set up our ObjectMonitor.h header first.

#import <Foundation/Foundation.h>

@interface ObjectMonitor : NSObject

- (id)initWithMonitoringObject:(NSObject *)objectToMonitor respondingWithSelector:(SEL)selector;

@end

We will implement a new initializer that accepts the object we want to monitor, and a selector which identifies a method that we will invoke on the objectToMonitor. What this will do, is allow ObjectMonitor to monitor our GameSettings and PlayerSettings objects (or any other object you provide it) and each object will tell ObjectMonitor what method to invoke. When a property on the observed class changes, the ObjectMonitor will invoke the requested method, within the provided object, in this case our settings classes. You'll see how it all comes together in the end. For now, we need to implement our ObjectMonitor.m implementation, so let's do that. First, the initializer and our selector property:

#import "ObjectMonitor.h"
#import <objc/objc-runtime.h>

@interface ObjectMonitor ()
@property (nonatomic) SEL selector;
@end

@implementation ObjectMonitor

- (id)initWithMonitoringObject:(NSObject *)objectToMonitor respondingWithSelector:(SEL)selector {
    self = [super init];

    if (self) {
        self.selector = selector;

        unsigned int count;
        objc_property_t *properties = class_copyPropertyList([objectToMonitor class], &count);

        for (size_t i = 0; i < count; ++i) {
            NSString *key = [NSString stringWithUTF8String:property_getName(properties[i])];
            [objectToMonitor addObserver:self forKeyPath:key
                  options:0 context:NULL];
        }

        free(properties);
    }
    return self;
}
@end

The first thing we do in our initializer is store a reference to the selector provided to us for future use. A selector is nothing more than a pointer to a method stored in a variable for us to use at a later date. This can cause some issues, which we will discuss and handle in just a bit.

Next, we have this interesting Objective-C Runtime function: class_copyPropertyList([objectToMonitor class], &count); Here we are calling the C runtime function class_copyPropertyList which takes a class as an argument and outputs the number of properties contained within that class. It also returns an array of every property that the class has. So we have the number of properties that the object has stored in the count variable and an array of every property the object has stored in the properties variable. What next you ask? We itterate through each property and observe them!

In our for-loop we use another C runtime function called property_getName which takes a property from our array and determines it's name and returns it for us. That provides us with a fully qualified property name that belongs to objectToMonitor. We then tell objectToMonitor that we are going to observe it for any changes made to that property. We then loop through the rest of the array, adding ourself as an observer to each property found in the object.

Finally we invoke the C runtime function free which releases the properties array from memory. Since C functions are not managed by ARC, we have to manage the memory ourselves.

A side note on observing the objectToMonitor for property changes. If you wanted, you could expand on this by adding a NSArray argument to this initializer with properties that you want exempt from observation. Then you could adjust your for-loop to be like this:

    for (size_t i = 0; i < count; ++i) {
        NSString *key = [NSString stringWithUTF8String:property_getName(properties[i])];

        // If this property is in our exempt array, we skip it and move on to the next.
        if ([exemptProperties containsObject:key]) {
            continue;
        }
        [objectToMonitor addObserver:self forKeyPath:key
                  options:0 context:NULL];
    }

Instead of providing a list of exemptions, you could just provide a list of properties to observe as well. This could be useful if you have several dozen properties but only want to monitor a handful. In most cases though, manually observing those using static KVO or the manual approach above is probably a better idea.

Alright, we have our initializer wrote and we are now observing any object that instances our ObjectMonitor! The next thing to do would be to do something when the properties are actually changed. Remember the observeValueForKeyPath method we implemented in our PlayerSettings class? Well, our ObjectMonitor class will implement that now, and our PlayerSettings and GameSettings classes won't have to implement this at all.

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    NSLog(@"%@ had %@ changed!", NSStringFromClass([object class]), keyPath);

    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[[object class] instanceMethodSignatureForSelector:self.selector]];
    if (invocation) {
        invocation.target = object;
        invocation.selector = self.selector;
        @try {
            [invocation invoke];
        }
        @catch (NSException *exception) {
            NSLog(@"Failed to invoke the method");
        }
        @finally {

        }
    } else {
        NSLog(@"ERROR: Failed to locate the method for @selector:%@", NSStringFromSelector(self.selector));
    }
}

The first thing we do is print to the debugger that we have entered the method due to an object's properties changing. The method arguments provides us with the property name and the object that the property belongs to. The property name is stored in keyPath and the object that owns the property is object. Since the method provides us with this information, we can print the name of the property using NSLog and include the class name. Since the object is not a string, we get the name of the class by using NSStringFromClass.

The next part can make or break your app, so you really want to make sure and set it up properly. We instance a NSInvocation object, which will be used to actually invoke the method provided to us and stored under self.selector. It's really important that you don't use [object performSelector:self.selector]; because this will not be memory safe and can leak. The runtime needs to know what to do with the result of your method invocation, which could be anything (void,BOOL, MYPlayerSettings). ARC would normally acquire this information from your objects header. With this approach, the ObjectMonitor class has no idea what method is stored in the selector, preventing the runtime from determining what the result is. This causes a compiler warning to be generated stating that a leak could occure. You could potentially acquire the actual method pointer itself by using IMP imp = [methodForSelector:self.selector]; but there is no guarantee that the correct method signature will be returned. So invoking imp could crash your app.

So, how do we get around this safely, keeping the runtime happy? We use NSInvocation. This is not a crash free solution but if you code it right and provide proper documentation on how to use the ObjectMonitor then you can have it work without a hitch.

The [NSInvocation invocationWithMethodSignature: instanceMethodSignatureForSelector:] checks the properties owner (object argument) to see if it has the selector we have a reference stored to. If it does, then a valid NSInvocation object is returned. If no method exists, then nil is returned. We check against nil on the very next line, so if no method is returned, we don't try to invoke it and crash out app.

We tell the invocation which object we want to invoke the method on ( invocation.target = object;) and then we tell it which method to invoke (invocation.selector = self.selector;). Lastly, we invoke the method using [invocation invoke]; and we wrap this invocation in a @try/@catch in the event that the invocation fails.

Using this approach, our ObjectMonitor is fairly safe to use. In the event an invalid method is provided we are protected from crashes. One last item to mention before we move on from our ObjectMonitor class. The [invocation invoke]; call invokes the method specified with zero arguments. What if you would like to know what property changed? You can do that by adjusting the if statement to look like this:

NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[[object class] instanceMethodSignatureForSelector:self.selector]];
if (invocation) {
    invocation.target = object;
    invocation.selector = self.selector;
    [invocation setArgument:&keyPath atIndex:2]; // index 0 = self and index 1 = _cmd
    @try {
        [invocation invoke];
    }
    @catch (NSException *exception) {
        NSLog(@"Failed to invoke the method");
    }
    @finally {

    }
} else {
    NSLog(@"ERROR: Failed to locate the method for @selector:%@", NSStringFromSelector(self.selector));
}

It is critical that the atIndex argument in the setArgument call is 2 or greater. The Objective-C runtime invokes all methods by send a message to the object. The message is objc_msgSend() and it requires at least two arguments. The receiver (in this case our object we are monitoring) and a selector. All Objective-C messages have two arguments that are always included. The first argument is passed as the receiver of the second agument (our method selector) and is most often passed as self. The second argument (selector) is passed as \_cmd and contains a pointer to our actual object method. So we can start adding arguments at index 2. If you would like to invoke the method we specify and provide the property name that was changed, you can do so. Your object that actually implements the method being invoked must have an argument that accepts what you add to the NSInvocation, otherwise it will fail to invoke. Luckily, since we wrapped it in a @try/@catch it won't crash your app. It can be annoying to debug though.

So, that's it, we have built our ObjectMonitor class. Now let's use it. We will return to our PlayerSettings class and re-write it to make use of this. We add our ObjectMonitor as a property and then in our PlayerSettings initializer, we instance it, tell it to observe us and we provide it our savePlayerSettings method for invocation.

#import "MYPlayerSettings.h"
#import "ObjectMonitor.h"

@interface MYPlayerSettings ()
@property (strong, nonatomic) ObjectMonitor *objectMonitor;
@end

@implementation MYPlayerSettings

-(id)init {
    self = [super init];
    if (self) {
        self.objectMonitor = [[ObjectMonitor alloc]
                              initWithMonitoringObject:self
                              respondingWithSelector:@selector(savePlayerSettings)];
    }
    return self;
}

- (void)savePlayerSettings {
    NSLog(@"%@ saved", self.name);
}

@end

And we do the same thing with our GameSettings class.

#import "MYGameSettings.h"
#import "ObjectMonitor.h"

@interface MYGameSettings ()
@property (strong, nonatomic) ObjectMonitor *monitor;
@end

@implementation MYGameSettings

- (id)init {
    self = [super init];
    if (self) {
        self.monitor = [[ObjectMonitor alloc]
                        initWithMonitoringObject:self
                        respondingWithSelector:@selector(saveGameSettings)];
    }
    return self;
}

- (void)saveGameSettings {
    NSLog(@"Game settings saved!");
}
@end

Isn't it nice that we can now add object property monitoring to any class we want with just 2 lines of code? A property declaration and instancing. Now we can use the following code, anywhere in our app:

self.playerSettings = [[MYPlayerSettings alloc] init];
self.playerSettings.name = @"Bob";
self.playerSettings.age = 22;
self.playerSettings.currentLevel = @"Last level";
self.playerSettings.mouseSensitivity = 5.0;

self.gameSettings = [[MYGameSettings alloc] init];
self.gameSettings.brightness = 73;
self.gameSettings.hardMode = NO;
self.gameSettings.version = @"1.0";

Our game settings and player settings will always be saved.

Of course we lost the ability to use our cool beginUpdates and endUpdates, but that can easily be reimplemented. You just re-set it back up and in your savem methods, don't actually save if performingBatchUpdates is YES.

I hope this document on implementing the ability to dynamically monitor any property on any object proves to be useful to you guys. It only took me about 5 hours to write!

Until next post.