Using Zombie Objects to Aid Debugging
Introduction I'd heard of zombie objects before, but only had a vague understanding — something like memory that hasn't been referenced but also hasn't been overwritten. NSZombie Object Cocoa provides a "Zombie Objects ()" feature. When enabled, the runtime converts all deallocated instances into special "zombie objects" rather than truly reclaiming them (a vivid metaphor — undead, not yet buried). The underlying memory of these objects cannot be reused and therefore cannot be overwritten. When a zombie object receives a message, it throws an exception that precisely identifies the message sent and describes the object as it was before deallocation. When you send a message to a deallocated object, a crash doesn't always occur: whether it crashes depends on whether the object's memory has been overwritten by something else. How to Enable In Xcode, set to YES to enable this feature. Specifically: open , select , switch to the tab, and check . How It Works The implementation lives in the OC library, the framework, and the framework (I haven't been brave enough to dig into those yet). When the system is about to deallocate an object and finds that zombie objects are enabled, it converts the object to a zombie instead of fully reclaiming it. Since reproducing zombie objects under ARC requires more complex setup, here's a simple MRC example: prints the class and superclass of an object. The output is: The object's class has changed from to . is generated at runtime: the first time a object needs to become a zombie, this class is created using the and registered in the class list. A zombie class is copied from a template class named . These zombie classes don't do much — they serve mainly as markers. The pseudocode below shows how the system creates a zombie class on demand, and how that class transforms the object being deallocated into a zombie: This is essentially what 's method does when — the runtime swaps out for the code above. By the end, the object's class has become . The key point: the object's memory is not freed (via ), so it cannot be reused. Of course, this is only a debugging aid — production code should never do this. The reason a new class is created for each zombie is so the system can identify the original class when a message is sent to the zombie. Otherwise all zombies would belong to and debugging would be impossible. The new class is created by the runtime function , which copies the entire class structure and assigns it a new name. The copy inherits the same superclass, instance variables, and methods. An alternative would be to subclass , but copying is more efficient. The zombie class's role becomes apparent during message forwarding. The class implements no methods and has no superclass — only an instance variable (required by all OC root classes such as ). Because it implements no methods, all messages sent to it go through the full forwarding mechanism. In the full message forwarding mechanism, is the core function — you'll see it in the call stack during debugging. The first thing it does is check the class name of the message recipient. If the name has the prefix , it means the recipient is a zombie object and needs special handling. At that point, a message like this is printed: This identifies the message sent to the zombie and the class it belonged to, then the application terminates. The original class name can be extracted from the zombie class name. If zombie objects are enabled in the example at the beginning of this post, and you send the message to the released instance, the output will be: Summary - During debugging, you can enable the zombie objects feature to convert objects that would normally be deallocated into zombie objects instead. - The system modifies the object's pointer to point to a zombie class. The zombie class responds to all messages by printing a message containing the selector name and the receiver, then terminating the application. References Effective Objective-C 2.0