Diagnosing EXC_BAD_ACCESS and Zombie Objects in Objective-C
Learn how to resolve EXC_BAD_ACCESS crashes in Objective-C by using Zombie Objects to identify dangling pointers and fixing improper reference types.
12 Aug 2026, 00:49 UTC

The Problem: Messaging Deallocated Instances
An EXC_BAD_ACCESS crash typically occurs when your code attempts to send a message to an object that has already been deallocated. This leaves a "dangling pointer"—a memory address that no longer points to a valid instance of a class. Because the memory may have been reclaimed by another process or marked as inaccessible, the application crashes immediately upon access.
The primary takeaway for resolving these crashes is to identify the exact moment of deallocation versus the moment of access. In Objective-C, this is most efficiently achieved using Zombie Objects, a diagnostic mode that prevents memory from being fully wiped, allowing the system to report exactly which class was accessed after its death.
Diagnostic Matrix: Identifying the Root Cause
| Symptom | Likely Cause | Diagnostic Indicator |
|---|---|---|
| Immediate crash on delegate callback | Strongly referenced delegate | Zombie log: message sent to deallocated instance of class 'X' |
| Crash during asynchronous block execution | Block capturing self weakly but not checked | self is nil or points to a zombie address |
| Memory growth followed by crash | Retain cycle masking a dangling pointer | Memory Graph Debugger shows circular strong references |
| Random crashes in high-memory pressure | Use of __unsafe_unretained | Crash occurs only after the referenced object is explicitly released |
Step-by-Step Resolution Process
1. Enable Zombie Objects
Standard memory management (ARC) hides the evidence of deallocation. To surface the error, you must enable the Zombie diagnostic tool in Xcode:
- Click the Scheme selector in the Xcode toolbar and select Edit Scheme...
- Navigate to the Run section in the left sidebar.
- Select the Diagnostics tab.
- Check the box for Zombie Objects.
- Run the application and reproduce the crash.
Risk: Zombie objects are never truly deallocated; they remain in memory to track illegal accesses. This will cause significant memory growth. Never enable this in a production build.
2. Analyze the Console Output
When the crash occurs with Zombies enabled, the Xcode console will no longer show a generic EXC_BAD_ACCESS. Instead, it will provide a specific warning:
*** -thread #1 -[MyCustomViewController updateUI]: message sent to deallocated instance 0x600003456789 of class 'MyDataModel'This log tells you three critical things: the method being called (updateUI), the address of the dead object, and the class of the object (MyDataModel).
3. Trace the Lifecycle
Once the class is identified, check the pointer declaration. A common failure point is the delegate pattern. Compare these two configurations:
Incorrect (Dangling Pointer Risk):
@property (nonatomic, assign) id<MyDelegate> delegate;Using assign (or __unsafe_unretained) does not notify the pointer when the object is deallocated. If the delegate is released, the pointer still holds the old address.
Correct (ARC Safe):
@property (nonatomic, weak) id<MyDelegate> delegate;The weak keyword ensures that the pointer is automatically set to nil when the object is deallocated, preventing the crash because sending a message to nil in Objective-C is a no‑op (does nothing) rather than a crash.
4. Inspect for Retain Cycles
If an object is staying alive longer than intended (masking a pointer issue) or causing leaks, use the Memory Graph Debugger. Click the "Debug Memory Graph" button (three circles connected by lines) in the debug bar. Look for cycles where Object A has a strong reference to Object B, and Object B has a strong reference back to Object A.
Verification and Rollback
To verify the fix, disable Zombie Objects in the Scheme and run the application through the same sequence of actions that triggered the crash. If the app remains stable and the Memory Graph Debugger shows the object is correctly deallocated when expected, the issue is resolved.
Rollback: If the change to weak references causes unexpected nil behavior (where logic fails because an object disappeared too early), revert the property to strong and implement a proper cleanup method to nullify the reference manually during the object's dealloc phase.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.