Quick & dirty object change tracking in C#

I ran into a issue the other day that was fairly straight forward to solve. I needed to see all of the properties that changed on a object since it was originally setup. The initial state of the object can be anything, either a freshly instanced object, a deserialized object or a object created by setting properties, one at a time during start up or something.

What I wanted to do was essentially perform a SQL update, that just updated the properties that were changed when the user presses a save button. So by creating a Initialize() method on my objects (or within a Load method or constructor, doesn't matter) I save a dictionary of the current objects property values like such:

public class MyModel
{
    private Dictionary<string, object> mOriginalValues = new Dictionary<string, object>();

    public void Initialize()
    {
        PropertyInfo[] properties = this.GetType().GetProperties();

        // Save the current value of the properties to our dictionary.
        foreach (PropertyInfo property in properties)
        {
            this.mOriginalValues.Add(property.Name, property.GetValue(this));
        }
    }
}

Pretty easy right? We just use a bit of reflection to grab the current state of the object and store it. Next, later on during the applications run-time when the user presses save, we essentially grab all of the current properties again. This time however, we compare the current properties to the previous ones and return a Dictionary of the properties that changed.

public Dictionary<string, object> GetChanges()
{
    PropertyInfo[] properties = this.GetType().GetProperties();
    var latestChanges = new Dictionary<string, object>();

    // Save the current value of the properties to our dictionary.
    foreach (PropertyInfo property in properties)
    {
        latestChanges.Add(property.Name, property.GetValue(this));
    }

    // Get all properties
    PropertyInfo[] tempProperties = model.GetType().GetProperties().ToArray();

    // Filter properties by only getting what has changed
    properties = tempProperties.Where(p => !Equals(p.GetValue(model, null), model.OrginalValues[p.Name])).ToArray();

    foreach (PropertyInfo property in properties)
    {
        latestChanges.Add(property.Name, property.GetValue(this));
    }

    return latestChanges;
}

If you wanted to, you could either re-initialize the model prior to returning the latest changes, or in the calling method, re-invoke the initialize method to set the models original state to the current state. Essentially, finalizing all of the changes and allowing you to track additional changes once again.

Within your app, you can use if (this.myModel.GetChanges().Count == 0) to determine if the model has changed at all, and therefore provide feedback to the user if they need to save or not. You can use this to compose update statements in SQL so you only update the fields you need, or use this to minimize the amount of data being sent over the cellular data in a Windows Phone 8 app.

This is the complete code I put into a abstract parent class that my objects will inherit from.

public abstract class MyBaseClass 
{
    private Dictionary<string, object> mOriginalValues = new Dictionary<string, object>();

    public void Initialize()
    {
        PropertyInfo[] properties = this.GetType().GetProperties();

        // Save the current value of the properties to our dictionary.
        foreach (PropertyInfo property in properties)
        {
            this.mOriginalValues.Add(property.Name, property.GetValue(this));
        }
    }

    public Dictionary<string, object> GetChanges()
    {
        PropertyInfo[] properties = this.GetType().GetProperties();
        var latestChanges = new Dictionary<string, object>();

        // Save the current value of the properties to our dictionary.
        foreach (PropertyInfo property in properties)
        {
            latestChanges.Add(property.Name, property.GetValue(this));
        }

        // Get all properties
        PropertyInfo[] tempProperties = model.GetType().GetProperties().ToArray();

        // Filter properties by only getting what has changed
        properties = tempProperties.Where(p => !Equals(p.GetValue(model, null), model.OrginalValues[p.Name])).ToArray();

        foreach (PropertyInfo property in properties)
        {
            latestChanges.Add(property.Name, property.GetValue(this));
        }

        return latestChanges;
    }
}

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.

Understanding Categories

What is a Category?

Objective-C has a really cool feature called Categories. In short, a Category gives developers an opportunity to add additional methods to existing classes. Does NSString not have a method you wish it did? You can go ahead and write the method and add it to NSString, without needing the original source code. Apple explains this in their documentation like such:

Any methods that you declare in a category will be available to all instances of the original class, as well as any subclasses of the original class. At runtime, there’s no difference between a method added by a category and one that is implemented by the original class.

One thing you want to watch out for however is properties. You can only add methods to an existing class via Categories; never properties. My tinkering around with them the other day showed that ivars are not allowed either. I'm not sure why this restriction is in place, but that's the way Apple designed them. You can add a property like @property (nonatomic, strong) NSString *foo; and access it without a problem; the compiler might complain a bit, but that can be silenced by adding a @dynamic above your property. All that does however is silence the warnings that your property is not being properly synthesized. Apple's documentation tells us that the class that the category is adding methods to, will not synthesize any properties in the categories. So the issue that comes from this is the inability to track the values associated with our properties, once the code leaves our setter/getter methods. The backing ivar's are never synthesized.

So long story short, don't use properties in your categories. If you need to use a property, extend the class with a class extension or sub-class it.

Putting it into practice

We now know what a category is, but what would you ever need to use one for? Once you get used to how categories work, it's surprisingly easy to find a use for them on a regular basis. For instance, tonight I ran into an issue with my code were I had two NSDate objects that had the same hour and minute, but were off by a few seconds because of the delay in creating the two dates. Since the seconds were irrelevant to what I was doing, I wanted a easy way to zero them out which would allow them to be equal to each other.

The most obvious answer is to use NSDateComponents, which is a real pain. It requires several lines of code that would need to be re-wrote over and over.

@implementation TestViewController
- (void)viewDidLoad
{
    [super viewDidLoad];

    // Get the current date.
    NSDate *date = [NSDate date];
    NSLog(@"Date is %@", date);

    // Only pull the year, month, day, hour and minute. Ignore seconds.
    NSUInteger unitFlags = (NSYearCalendarUnit
                            | NSMonthCalendarUnit
                            | NSDayCalendarUnit
                            | NSHourCalendarUnit
                            | NSMinuteCalendarUnit);

    // Instance our calendar
    NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
    // Filter out what components we want from the date
    NSDateComponents *dateComponents = [calendar components:unitFlags fromDate:date];

    // Adjust the date using our date components
    NSDate *correctedDate = [calendar dateFromComponents:dateComponents];
    NSLog(@"Corrected date is %@", correctedDate);
}
@end

This provides me with the following output, which verifies that the code is indeed, zeroing out the seconds.

[6755:60b] Date is 2013-11-22 08:05:25 +0000

[6755:60b] Corrected date is 2013-11-22 08:05:00 +0000

Now, instead of re-using this code all over the place, I can make a helper method in what ever object is using this. Although, I might want to use this in other classes, or even in another project! So why don't I move the code over to a category instead? Let's do that, using the same code above.

You add a new category file to your project via File->New->File and selecting a Objective-C Category under the Cocoa Touch or Cocoa platforms; depending on if you are developing on iOS or OS X.

Press next and give it a category name of MyDateCategory. For the Category On field, type in NSDate and press Next and save the file to your project folder.

Alright, now we have two new files. A NSDate+MyDateCategory.h header file and a NSDate+MyDateCategory.m implementation file. Let's start off with the header file which should look like this:

#import <Foundation/Foundation.h>

@interface NSDate (MyDateCategory)

@end

You can see that the actual header is an interface for NSDate. Our custom category is shown in parentheses, which lets the compiler know we are adding stuff to the existing NSDate class. We want to add our code to zero out the seconds right? So we need a new instance method which we will call dateWithZeroSeconds.

#import <Foundation/Foundation.h>

@interface NSDate (MyDateCategory)
- (NSDate *)dateWithZeroSeconds;
@end

Our new instance method returns a new NSDate, which will let us just invoke this method and assign our date with the adjusted date. The implementation then would look like this in the .m file.

#import "NSDate+MyDateCategory.h"

@implementation NSDate (MyDateCategory)
- (NSDate *)dateWithZeroSeconds {
    // Get the current date.
    NSLog(@"Date is %@", self);

    // Only pull the year, month, day, hour and minute. Ignore seconds.
    NSUInteger unitFlags = (NSYearCalendarUnit
                            | NSMonthCalendarUnit
                            | NSDayCalendarUnit
                            | NSHourCalendarUnit
                            | NSMinuteCalendarUnit);

    // Instance our calendar
    NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
    // Filter out what components we want from the date
    NSDateComponents *dateComponents = [calendar components:unitFlags fromDate:self];

    // Adjust the date using our date components
    NSDate *correctedDate = [calendar dateFromComponents:dateComponents];
    NSLog(@"Corrected date is %@", correctedDate);

    return correctedDate;
}
@end

It's the same code that we wrote above right? With the exception that now we can use it in any project that has our NSDate+MyCustomDate.h and .m files included. Now one of the important differences here however is to note my use of the keyword self. Since we are adding this method to NSDate, it makes since that I need to get the date components from myself, because I am the date at this point. Not another object, so we ask for the components from self. You'll want to remember that when you are writing a category method, all of the class properties and methods are available to you; just be sure to access them via the self keyword.

So returning to our ViewController, we would use this method like such:

- (void)viewDidLoad
{
    [super viewDidLoad];

    NSDate *date = [NSDate date];
    NSDate *correctedDate = [date dateWithZeroSeconds];
}

And we can shorten this up even more by passing our dateWithZeroSeconds method the returned object from [NSDate date]

- (void)viewDidLoad
{
    [super viewDidLoad];

    NSDate *correctedDate = [[NSDate date] dateWithZeroSeconds];
}

Really nice right? Now anywhere in your code, you can use your custom method just like it was part of a NSDate object, without having to sub-class NSDate!

Now, I said earlier that using the NSDateComponents object is more work than what you really need to do for something like this, so I'm going to revise our Category method with something more elegant.

Like such:

- (NSDate *)dateWithZeroSeconds
{
    NSTimeInterval time = floor([self timeIntervalSinceReferenceDate] / 60.0) * 60.0;
    return  [NSDate dateWithTimeIntervalSinceReferenceDate:time];
}

Now isn't that prettier?

Hopefully this helps those that read it understand Categories in Objective-C.

Getting return values from a code block

I've never used any async stuff during my time as a developer, so when I came across blocks in Objective-C I was stumped for a bit as to how they work. _Why can't I access any local fields or return this value!_. Now that I have had some exposure I'm more familiar with them and understand how they work. I still have the same issue though; there are times I need to get a value back out of the block. Yes I can post to NSNotificationCenter, but I'd rather not.

I learned you can create an instance of a code block, in which case you can return a value and capture it. As an example, let's say we want to build an array of buttons currently referenced by our view controllers view. I know that you wouldn't really need a block to do something like this. In the real-world I would probably just use the `[NSArray enumerateObjectsUsingBlock:];` method for the purpose of demonstrating return values in blocks though, it'll work.

Read More