iOS 调试 解决EXC_BAD_ACCESS错误
做iOS 程序开发时经常用遇到 EXC_BAD_ACCESS 错误导致 Crash,出现这种错误时一般 Xcode 不会给我们太多的信息来定位错误来源,只是在应用 Delegate 上留下像Thread 1: Program received signal:“EXC_BAD_ACCESS”,让问题无从找起。
比如你对已释放的对象发送消息时就会出现,EXC_BAD_ACCESS,再如release 的对象再 release,release 那些autorelease 的对象等也会报这样的错。默认设置下 Xcode 不会给你定位具体是哪一行代码,不该去使用已释放的对象,或者release 用错了。
比如下面这段代码:
1 | - (void)drawRect:(CGRect)rect |
上面的代码就会出现EXC_BAD_ACCESS 错误:
错误提示是:
CoreGraphics`CGColorGetAlpha:
0x31c1d780: vmov.i32d0, #0x0
0x31c1d784: cmp r0, #0
0x31c1d786: itttt ne
0x31c1d788: ldrne r1, [r0, #28]
0x31c1d78a: addne.wr0, r0, r1, lsl #2
0x31c1d78e: ldrne r0, [r0, #28]<-
0x31c1d790: vmovne d0, r0, r0
0x31c1d794: vmov r0, s0
0x31c1d798: bx lr
0x31c1d79a: nop
会定位到红色的那一行,这个实在是让人头痛,不过还是有解决办法的。
NSZombieEnabled环境变量可以帮我们的忙,就是当 设置NSZombieEnabled环境变量后,一个对象销毁时会被转化为_NSZombie,设置NSZombieEnabled后,当你向一个已经释 放的对象发送消息,这个对象就不会向之前那样Crash或者产生一个难以理解的行为,而是放出一个错误消息,然后以一种可预测的可以产生debug断点的 方式消失, 因此我们就可以找到具体或者大概是哪个对象被错误的释放了。
对 Xcode 设置了NSZombieEnabled 之后,
错误提示是:
*** -[Not A Type retain]: message sent to deallocated instance 0x2009b480
应该是内存泄露的问题。
修改代码后,代码如下:
1 | - (void)drawRect:(CGRect)rect |
一切正常了。成功了!
如何设置 NSZombieEnabled 呢,在 Xcode3 和 Xcode4 下设置不一样,Xcode4 下设置很简单。
Xcode3 下 NSZombieEnabled 设置方法如下:
- 在XCode左边那个Groups& Files栏中找到Executables,双击其中的一项,或者右键Get Info;
- 切换到Arguments
- 这里一共有两个框,在下面那个Variables to be set in theenvironment:点+号添加一项,Name里填NSZombieEnabled,Value填Yes,要保证前面的钩是选中的。
Xcode4 下设置 NSZombieEnabled 的方法:
你可以点击 Xcode4 菜单 Product -> Edit Scheme-> Arguments, 然后将点击”加号”, 将 NSZombieEnabled 参数加到Environment Variables 窗口中, 后面的数值写上 ”YES”.
或者在 Xcode4 菜单 Product -> EditScheme -> Diagnostics 设置窗口中直接勾上Enable ZombieObjects 即可,Xcode 可用 cmd+shift+< 进到这个窗口。
讲Cocoa技术十分专业的网站之一,下面的链接详细讲了讲NSZombieEnable的原理。http://www.cocoadev.com/index.pl?NSZombieEnabled
苹果官方的Mac OS X Debugging Magic,详细讲述了最为一个高级苹果程序员应该具备的调试技巧 http://developer.apple.com/library/mac/#technotes/tn2004/tn2124.html
其实还可以在Instruments中开启NSZombie选项,这样就可以在Instruments中直接查看crash时候的callstack了:http://www.markj.net/iphone-memory-debug-nszombie/
最后提醒NSZombieEnabled只能在调试的时候使用,千万不要忘记在产品发布的时候去掉,因为NSZombieEnabled不会真正去释放dealloc对象的内存,一直开启后果可想而知,自重!