Garbage Collection and KVO and NSNotificationCenter, Oh My!

Recently, I ran into some problems with data dependencies and garbage collection in my bespoke model classes. Several classes in my model code register as observers of other model classes using either KVO or via NSNotificationCenter.

Although I’m using garbage collection, I was trying to be a good boy by implementing a dispose method on some of these model classes so that they could de-register as observers when they were no longer an active participant of the model.

The problem with this approach is that various Cocoa classes, such as NSUndoManager, don’t easily offer me the opportunity to invoke my dispose method (say for example when the redo queue is cleared by registering a new invocation). There are a few different solutions to the problem. In my case, registering the “activation” and “deactivation” of my model classes (i.e. the adding and removing of themselves as observers) with the NSUndoManager is the one I opted for.

In the course of implementing this code, I played around with using NSObject's finalize method as a way to de-register my observers, rather than inducing the undo manager to invoke my own dispose method. Generally speaking, I try to stay away from finalize methods in garbage collected languages, and Objective-C is no exception. Apple cautions against the madness of using finalize methods for performing any serious work.

All that said, it got me thinking about where exactly Apple expects objects to de-register their KVO and NSNotificationCenter observers in the general case. The problem with remove observers in finalize methods are several:

  1. You are forced to introduce a finalize method where you otherwise wouldn’t have to. Not good for performance and can open up a Pandora’s box of non-determinism.
  2. The finalizer’s of objects collected in the same cycle are not guaranteed to run in any particular order. So de-registering object A as an observer of object B in object A’s finalize method might happen after object B is already collected (i.e. when it’s invalid), leading to “Cannot remove an observer … for the key path … from … because it is not registered as an observer.”
  3. The de-registration of observers happens at a non-deterministic time (i.e. whenever the garbage collector gets around to running).

As far as items #1 and #2 are concerned, there is a better way, which is (wait for it…) to do nothing.

From the docs on NSNotificationCenter's addObserver:

“As a convenience, when built with garbage collection, you do not need to remove any garbage collected observer as the system will do it implicitly.”

And as of the November seed of 10.6, this is also now true of KVO. The seed note “Automatic Removal of Finalized Key-Value Observers When Running Garbage-Collected (New since November seed)” describes the following:

“In Mac OS 10.6, explicit removal of observers when they’re finalized is now never necessary. KVO automatically removes observers as they’re collected. Actually, in Mac OS 10.6 all invocations of -[NSObject(NSKeyValueObserverRegistration) removeObserver:forKeyPath:] and -[NSArray(NSKeyValueObserverRegistration) removeObserver:fromObjectsAtIndexes:forKeyPath:] do virtually nothing when either the receiver or the observer is being finalized (so it’s not very bad for performance to leave them there in applications that still have to run on Mac OS 10.5).”

So at least in Snow Leopard after the November seed, you can just let it ride and the system will take care of cleaning up your KVO and NSNotificationCenter registrations.

As for item #3, well it’s either playing games with regularly invoking NSGarbageCollector's collectExhastively or implementing a dispose pattern.

Published in: on October 23, 2009 at 8:56 am  Comments (1)  
Tags: , , , ,

Pattern Muscle Memory

Sometimes I catch myself applying a particular pattern in an architecture / application design almost without consciously thinking about it. One of the side benefits of flailing around in Cocoa and Objective-C is the disruption of my “pattern muscle memory”.

A recent example is in the implementation of the model layer of the graphical authoring tool I’m working on. One very basic required feature is the ability to delete a graphical element. The element can be deleted in the usual ways: the user pressing the delete key, the user selecting delete from the edit menu, etc. These actions ultimately invoke the same code and remove the item from the data model. There are other ways that the element might be deleted too: as a result of a cut operation, as the result of a synchronization or merge operation with an external source (like importing data from another file), etc.

My pattern muscle memory would normally have me tackle the ability to undo/redo this operation using classic “gang of four” style command and memento patterns; creating concrete implementations of an abstract command for each action.

In this case, being a new Cocoa programmer, my C#/.NET pattern muscle memory was disrupted by the NSUndoManager’s cool ability to package up “ordinary” invocations rather than requiring any special sub-classing or glue code. Now, I could certainly implement this kind of thing in .NET using reflection, etc, but I would have to implement it rather than using it “off the shelf”. Besides, my pattern muscle memory would have kicked in before I really had given it too much thought.

For my delete implementation, I chose to use one of the common Cocoa approaches exemplified in Aaron Hillegass’ book of using KVO to observe the removal of the object from it’s collection and registering it’s inverse (an insertion) on the undo stack. When KVO tells me that the object was added, I register the inverse of that operation (a deletion) on the undo stack.

One common challenge I’ve faced in the past is what to do about side effects or otherwise dependent actions of the principal action occurring. In the case of deleting my graphical object, there are several side such effects. One example is the need to modify connected or constrained graphical objects. In certain situations, an extreme side effect is the need to delete or create related objects, allocate unique identifiers, etc.

My pattern muscle memory typically has me encapsulate these dependent actions along with the implementation of my original user operation, in this case, the delete. One complication of this approach is that as the application scales and multiple developers work on the code base, it can be hard to ensure that everyone consuming the model uses the same high level delete implementation.

In the case of the delete key, the delete menu item, etc, it’s dead simple to make sure the same high level code (with it’s encapsulated side effect behavior) is invoked. As more complex features (such as the merge example) find themselves needing to delete the graphical object (along with performing all of it’s side effects) the requirement to use the common high level delete implementation can get obscured. Especially when the various types of consumers haven’t been anticipated in the design of the model’s API. I’ve found the murkiness (and naughtiness) increases when a generalized data model framework is being used, as it invites direct access to manipulating the data model via it’s APIs. Such is definitely the case with Core Data.

In the past, my pattern muscle memory has had me implement intermediate model layers that encode the side effects, but it has always seemed like such a waste of time and effort to obfuscate direct access to the generalized data model framework APIs with intermediate layers.

In my Cocoa implementation, I choose to implement my data model side effects in KVO observers. I did this so that deleting my graphical element using the Core Data API results in the requisite side effects and dependent data model changes. The main reason I did this is to avoid requiring special discipline or knowledge of the side effects on the part of the consumer of the model. My KVO observers however, also implement the undo/redo registration as I touched upon above. The problem with this approach when naively done is the exponential accumulation of invocations in the undo and redo queues. Each KVO notification from successive undo and redo operations results in performing the side effects and dependent operations anew (in addition to the queued undo/redo invocation).

This is easily rectified by checking to see if our KVO notification is happening as a result of an undo/redo operation (i.e. [NSUndoManager isUndoing] or [NSUndoManager isRedoing]) before performing side effects and dependent operations. If we are in the midst of an undo or redo, we let the invocations on the queue take care of the side effects and dependent operations, but don’t ourselves reissue them as a result of the KVO notification.

Sure I could have implemented all this in C# / .NET, or Java or JavaScript for that matter, but it’s not set up “out of the box” for it, and besides, my pattern muscle memory would have kicked in first anyway.

The bottom line result is a clear and compact model implementation that is easier to implement, extend and maintain over time, one that does not constrain it’s consumers to specialized APIs or require specialized knowledge.

Well, at least that is the theory… Time will tell, but for now I am love-hating my pattern muscle memory disruption.

Published in: on May 20, 2009 at 11:28 pm  Comments (1)  
Tags: , , , ,
Follow

Get every new post delivered to your Inbox.