In Objective-C, messaging a
nil reference is a no-op, so if we consider the following class:
The following code snippet will not print anything, but it also won’t crash:
Since Objective-C is a dynamic language, we can represent message invocations with objects and invoke them at runtime.
NSInvocation makes that easy, and it also respects the convention that messaging a
nil object is a no-op:
Again, the code snippet above won’t print anything, but it also won’t crash.
This also holds for weak references. If
steve were a weak reference that got
nil-ified before the invocation was dispatched, this code would fail gracefully. Here’s an example for completeness:
The code above also doesn’t print anything, since
steve is assigned to
If you’re not familiar with
NSUndoManager, it is the primary class used when implementing undo support in Cocoa and Cocoa Touch apps. Every time you perform an action that could be un-done, you register it with an instance of
NSUndoManager. The “action” is simply a method (or block) that gets invoked when the user presses “Undo”. Here’s a basic example that registers our
-undoSomething method, which reverses whatever
Actions are stored and invoked in LIFO (last-in first-out) order, so if we were to press “Undo” now,
-[NSUndoManager undo] would end up invoking
In fact, if you look at the stack trace at the time
-undo is invoked, you’ll notice
NSUndoManager internally simply keeps around a stack of
NSInvocations to dispatch with each undo operation.
If you take a look at the documentation for
-prepareWithInvocationTarget:, you’ll notice it claims the “undo manager maintains a weak reference to the target”. As I learned the hard way with some new crashes in Pixen, that is actually not true.
Consider the following example:
steve falls out-of-scope before
-[NSUndoManager undo], its pointee gets deallocated. When the call to
undo is finally made, we get a crash, and if we turn on Zombie Objects in the Scheme Editor, we can confirm
NSUndoManager is trying to talk to a deallocated object:
This crash would not occur if
NSUndoManager kept around a weak reference to our
Person since it would have been
nil-ified, and as we convinced ourselves above, sending an
NSInovcation to a
nil target is perfectly safe. Thus, contrary to the documentation,
NSUndoManager does not actually store weak references to undo targets.