EXC_BAD_ACCESS crashes are among the most frustrating errors in iOS development. You see a memory address, no stack trace, no hints — just a blank console staring back at you. Later I learned that this crash is usually caused by accessing a deallocated object — a “dangling pointer” problem. And Xcode’s “Zombie Objects” debugging tool is specifically designed to handle this.
What Is a Zombie Object
In Objective-C, when an object’s reference count drops to zero, it’s normally deallocated and its memory is reclaimed. If code later tries to send a message to that memory — calling a method or accessing a property — it triggers an EXC_BAD_ACCESS crash.
This happens because the pointer still exists, but the object doesn’t. The memory might have been reallocated to other data, so accessing it leads to unpredictable behavior.
The principle of Zombie Object debugging: When you enable Zombie detection, the Runtime changes its behavior — when an object’s reference count reaches zero, the system doesn’t actually reclaim its memory. Instead, it “turns” the object into an _NSZombie.
This zombie object exists for one purpose: if anyone sends a message to it, it throws an exception and tells you “you sent a message to a deallocated instance.”
*** -[HyClass description]: message sent to deallocated instance 0x1b95990
This is a typical zombie warning. It tells you that you tried to send the description message to an instance of HyClass at address 0x1b95990, but it had already been deallocated. This information, though brief, is far more useful than a single crash address.
How to Enable Zombie Object Detection
There are two ways to enable Zombie detection in Xcode.
Method 1: Enable in the Scheme (most common)
Click the Scheme name in the top-left corner of Xcode (next to the Run button), select Edit Scheme…. In the popup, select the Run tab, go to the Diagnostics panel, and check Zombie Objects.
Method 2: Use Instruments’ Zombies template
Launch Instruments via Product → Profile and select the Zombies template. After running and reproducing the crash, Instruments will show a dialog. Clicking the arrow lets you view the object’s complete lifecycle — where it was created, retained, and released.
Getting More Information with Environment Variables
In the Scheme’s Arguments panel, you can add two environment variables to enhance debugging:
NSZombieEnabled = YESMallocStackLoggingNoCompact = YES
The second variable makes Xcode record the allocation history for every memory address. Combined with LLDB’s bt command to print stack traces, you can trace back to the specific code line.
How to Read Zombie-Related Crash Reports
In crash reports, these patterns are typical signs of zombie objects:
Pattern 1: objc_msgSend at the top of the stack
Thread 0 Crashed:
0 libobjc.A.dylib 0x00000001a186d190 objc_msgSend + 16
1 Foundation 0x00000001a1f31238 __NSThreadPerformPerform + 232
...
objc_msgSend is the message-sending entry point. When it appears at the top of the stack, the runtime is trying to send a message to an object that no longer exists.
Pattern 2: objc_release at the top of the stack
Thread 2 Crashed:
0 libobjc.A.dylib 0x00007fff7478bd5c objc_release + 28
1 libobjc.A.dylib 0x00007fff7478cc8c AutoreleasePoolPage::pop + 726
This indicates the runtime is trying to release an already-deallocated object — an “over-release” issue.
Pattern 3: doesNotRecognizeSelector:
Last Exception Backtrace:
0 CoreFoundation __exceptionPreprocess + 220
1 libobjc.A.dylib objc_exception_throw + 55
2 CoreFoundation -[NSObject doesNotRecognizeSelector:] + 139
Sometimes zombie memory gets reused by a new object. The new object’s type is different from the original, so when it receives a message it can’t handle, it triggers doesNotRecognizeSelector:. The console prints something like -[NSNumberFormatter playSound]: unrecognized selector. playSound clearly doesn’t belong to NSNumberFormatter — indicating that memory once held an object that could play sound, but it’s long gone.
Important Considerations When Enabling Zombie Detection
1. Only for debugging
Zombie detection is a debugging tool — never enable it in release builds. It keeps all deallocated objects in memory, causing memory usage to spike. Make sure to turn it off in Release mode.
2. Works under ARC too
ARC manages reference counting automatically, but dangling pointer issues can still occur — for example, with assign or unsafe_unretained object pointers, or with Core Foundation objects where reference counting isn’t properly managed. ARC doesn’t solve all memory issues, so zombie detection is still useful in ARC projects.
Summary
The core principle of zombie object detection: deallocated objects “rise from the dead” as _NSZombie, providing a clear error location when subsequent messages are sent. Combined with MallocStackLoggingNoCompact and Instruments’ Zombies template, you can trace the object’s complete lifecycle. Understanding this tool makes EXC_BAD_ACCESS crashes far less intimidating.

GitHub source code address:https://github.com/2252408699/ZombieObjectDebugDemo.git
