Saturday, February 26, 2011

NSZombieEnabled = YES: One little hint that may help you a lot!

Hi friends,

Recently, I've been stuck on a crash in one of my iOS applications and I hadn't known why it was happening. I had opened Xcode Console for clues, but the application simply crashed without any apparent error messages or warnings.

I decided to go to Applications > Utilities > Console.app and I saw this information below:


25/02/11 22:44:08 SpringBoard[913] Application 'Velasco' exited abnormally with signal 10: Bus error
25/02/11 22:44:08 ReportCrash[918] Saved crash report for Velasco[915] version ??? (???) to /Users/marcelo_palermo/Library/Logs/DiagnosticReports/Velasco_2011-02-25-224408_MacBook-de-Marcelo-Palermo.crash

By looking at the crash file, I found this:

Process: Velasco [915]
Path: /Users/marcelo_palermo/Library/Application Support/iPhone Simulator/4.2/Applications/1319C807-6DE2-4CD4-9E3E-1CC50A50EC84/Velasco.app/Velasco
Identifier: Velasco
Version: ??? (???)
Code Type: X86 (Native)
Parent Process: launchd [183]

Date/Time: 2011-02-25 22:44:07.777 -0300
OS Version: Mac OS X 10.6.6 (10J567)
Report Version: 6

Exception Type: EXC_BAD_ACCESS (SIGBUS)
Exception Codes: KERN_PROTECTION_FAILURE at 0x0000000000000008
Crashed Thread: 0 Dispatch queue: com.apple.main-thread

Application Specific Information:
objc_msgSend() selector name: release
iPhone Simulator 235, iPhone OS 4.2 (iPhone/8C134)

Thread 0 Crashed: Dispatch queue: com.apple.main-thread
0 libobjc.A.dylib 0x0116aa63 objc_msgSend + 23
1 UIKit 0x003aef1d -[UINavigationController setDisappearingViewController:] + 64
2 UIKit 0x003ac4f6 -[UINavigationController _clearLastOperation] + 56
3 UIKit 0x003ace3f -[UINavigationController navigationTransitionView:didEndTransition:fromView:toView:] + 852
4 UIKit 0x00539e23 -[UINavigationTransitionView _notifyDelegateTransitionDidStopWithContext:] + 345
5 UIKit 0x0053afd2 -[UINavigationTransitionView _cleanupTransition] + 712
6 UIKit 0x00326665 -[UIViewAnimationState sendDelegateAnimationDidStop:finished:] + 294
7 UIKit 0x003264f7 -[UIViewAnimationState animationDidStop:finished:] + 77
8 QuartzCore 0x01eef6cb run_animation_callbacks(double, void*) + 278
9 QuartzCore 0x01eef589 CA::timer_callback(__CFRunLoopTimer*, void*) + 157
10 CoreFoundation 0x00fe9fe3 __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 19
11 CoreFoundation 0x00feb594 __CFRunLoopDoTimer + 1220
12 CoreFoundation 0x00f47cc9 __CFRunLoopRun + 1817
13 CoreFoundation 0x00f47240 CFRunLoopRunSpecific + 208
14 CoreFoundation 0x00f47161 CFRunLoopRunInMode + 97
15 GraphicsServices 0x0193d268 GSEventRunModal + 217
16 GraphicsServices 0x0193d32d GSEventRun + 115
17 UIKit 0x0030442e UIApplicationMain + 1160
18 Velasco 0x0000264c main + 102 (main.m:14)
19 Velasco 0x000025dd start + 53


My application is Navigation-based. So, the crash pointed that something was wrong when popping back to RootController. The objc_msgSend indicates much probably I was trying to pass a message to an already released object. But this was not enough to detect where issue happened and more: how to solve the crash.

By researching on iOS, I discovered the NSZombie object that definitely would help me. Enabling it is surprisingly easy:

1) In Xcode, set your building configuration to Debug
2) In Xcode treeview, look for Your_Project_Name > Executables > Your_Executable_Name and right-click it > Get Info
3) Look for the following screen:

4) As shown in above picture (click it to enlarge), within the "Variables to be set in the environment", click on the + button at bottom-left corner and add the following information:

Name: NSZombieEnabled
Value: YES

5) Recompile the application and force/reproduce the crash again
6) After the crash occurs, go back to Applications > Utilities > Console.app
7) Now you will see a more detailed information:

25/02/11 22:50:48 com.apple.launchd.peruser.501[183] (UIKitApplication:com.marcelopalermo.Velasco[0xa040][1010]) Job appears to have crashed: Bus error
25/02/11 22:50:48 SpringBoard[913] Application 'Velasco' exited abnormally with signal 10: Bus error
25/02/11 22:50:48 ReportCrash[1013] Saved crash report for Velasco[1010] version ??? (???) to /Users/marcelo_palermo/Library/Logs/DiagnosticReports/Velasco_2011-02-25-225048_MacBook-de-Marcelo-Palermo.crash
25/02/11 22:57:35 SpringBoard[913] Reloading application state for 'com.marcelopalermo.Velasco' as its modification date has changed
25/02/11 22:57:35 SpringBoard[913] Reloading and rendering all application icons.
25/02/11 22:57:51 Velasco[1041] *** -[UIProgressView release]: message sent to deallocated instance 0xeb08c70
25/02/11 22:57:52 com.apple.launchd.peruser.501[183] (UIKitApplication:com.marcelopalermo.Velasco[0x24e7][1041]) Job appears to have crashed: Trace/BPT trap
25/02/11 22:57:52 SpringBoard[913] Application 'Velasco' exited abnormally with signal 5: Trace/BPT trap
25/02/11 22:57:52 ReportCrash[1044] Saved crash report for Velasco[1041] version ??? (???) to /Users/marcelo_palermo/Library/Logs/DiagnosticReports/Velasco_2011-02-25-225752_MacBook-de-Marcelo-Palermo.crash

The first hint of the crashed appeared (see the text in bold): An UIProgressView object was sent a release message, but it was already deallocated. Now look at the .crash new report. Here's the information:

Process: Velasco [1041]
Path: /Users/marcelo_palermo/Library/Application Support/iPhone Simulator/4.2/Applications/1319C807-6DE2-4CD4-9E3E-1CC50A50EC84/Velasco.app/Velasco
Identifier: Velasco
Version: ??? (???)
Code Type: X86 (Native)
Parent Process: launchd [183]

Date/Time: 2011-02-25 22:57:51.823 -0300
OS Version: Mac OS X 10.6.6 (10J567)
Report Version: 6

Exception Type: EXC_BREAKPOINT (SIGTRAP)
Exception Codes: 0x0000000000000002, 0x0000000000000000
Crashed Thread: 0 Dispatch queue: com.apple.main-thread

Application Specific Information:
iPhone Simulator 235, iPhone OS 4.2 (iPhone/8C134)

Thread 0 Crashed: Dispatch queue: com.apple.main-thread
0 CoreFoundation 0x00f7a057 ___forwarding___ + 183
1 CoreFoundation 0x00f79f22 _CF_forwarding_prep_0 + 50
2 Velasco 0x000153b5 -[BlogDetailView dealloc] + 293 (BlogDetailView.m:710)
3 UIKit 0x003aef1d -[UINavigationController setDisappearingViewController:] + 64
4 UIKit 0x003ac4f6 -[UINavigationController _clearLastOperation] + 56
5 UIKit 0x003ace3f -[UINavigationController navigationTransitionView:didEndTransition:fromView:toView:] + 852
6 UIKit 0x00539e23 -[UINavigationTransitionView _notifyDelegateTransitionDidStopWithContext:] + 345
7 UIKit 0x0053afd2 -[UINavigationTransitionView _cleanupTransition] + 712
8 UIKit 0x00326665 -[UIViewAnimationState sendDelegateAnimationDidStop:finished:] + 294
9 UIKit 0x003264f7 -[UIViewAnimationState animationDidStop:finished:] + 77
10 QuartzCore 0x01eef6cb run_animation_callbacks(double, void*) + 278
11 QuartzCore 0x01eef589 CA::timer_callback(__CFRunLoopTimer*, void*) + 157
12 CoreFoundation 0x00fe9fe3 __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 19
13 CoreFoundation 0x00feb594 __CFRunLoopDoTimer + 1220
14 CoreFoundation 0x00f47cc9 __CFRunLoopRun + 1817
15 CoreFoundation 0x00f47240 CFRunLoopRunSpecific + 208
16 CoreFoundation 0x00f47161 CFRunLoopRunInMode + 97
17 GraphicsServices 0x0193d268 GSEventRunModal + 217
18 GraphicsServices 0x0193d32d GSEventRun + 115
19 UIKit 0x0030442e UIApplicationMain + 1160
20 Velasco 0x000024fc main + 102 (main.m:14)
21 Velasco 0x0000248d start + 53

Now the search is complete: the crash reports pointed the offending code at line 710 of BlogDetailView.m. My code at this line was:

(void)dealloc {

//[blogDetailsWebView setDelegate:nil];

[blogDetailsWebView release];

[activ release];

[storiesBookMarksArray release];

[selfButton release];

[pdfFileName release];

[pgv release]; <---- my offending code here!!!!

[receivedData release];

[super dealloc];

}


I needed to comment [pgv release]; out of my code to make it all work fine. And it made total sense. That's because "pgv" is my UIProgressView. Actually, inside one of my methods I have a UIProgressView inside an UIAlertView. And I had coded the following:

pgv = [[[UIProgressView alloc] initWithFrame:CGRectMake(30.0f, 80.0f, 225.0f, 90.0f)] autorelease] ;

[myAlertView addSubview:pgv];


Since it's set to autorelease, I wanted to use this object till the very end of my method. After that, the pgv object is added to autorelease pool. That means when that method lifecycle ends, the autorelease pool is flushed [1]. The (void) dealloc method is called only when exiting my BogDetailView (which of course happens much after my method where I allocate the pgv). But I simply forgot I was using autorelease for my pgv.

At this point, I was issuing a new [pgv release] to an already released pgv. That causes a crash.

The conclusion is: NSZombieEnabled may really help you in situations like the above one. I really consider it's worth to give a try! I hope this article may help you.

Also, don't forget to consult Apple's Objective-C Memory Management Programming Rules [2].

Best Regards

Marcelo

Reference:
[1] O'REILLY MacDevCenter.com. Memory Management in Objective-C.
[2] APPLE, INC. Memory Management Rules.