一.EXC_BAD_ACCESS问题
当向一个已经释放的对象发送消息时,就会使程序崩溃,但调试器仅打印出EXC_BAD_ACCESS错误,而没有其它信息提示是哪个对象导致的。
为了找到那那个提前被释放的对象,可以启动NSZombie。启用NSZombie后,对象被完全释放后就转化为NSZombie,但其内存并没有被释放,仍然可用。这时候向NSZombie(即已被完全释放的对象)发送消息时,因内存仍可用,程序不会crash,而调试器此时也可得知此已释放对象的地址,类型以及发向其的消息是什么。
二.解决方法
有两种方式启用NSZombie,一是设置NSZombieEnabled环境变量,二是使用Instruments的Zombies检测。
2.1 示例代码
以iphone-memory-debug-nszombie中的ZombieDebug Demo Project代码来演示这两种方法,异常位置自己去找。
@implementation ZombieDebugViewController
@synthesize objArray;
-(void)rewriteText {
NSMutableString* s = [NSMutableString stringWithCapacity:100];
for (id obj in objArray) {
[s appendFormat:@"%@,\n",obj];
[obj release];
}
label.text = s;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.objArray = [NSMutableArray arrayWithCapacity:10];
[objArray addObject:@"I'm a string object"];
[self rewriteText];
}
-(IBAction) tapButton:(id)button {
NSNumber* n = [NSNumber numberWithLong:random()];
[objArray addObject:n];
[self rewriteText];
}
-(void)dealloc {
[super dealloc];
self.objArray=nil;
}
@end
2.2.方法1-启用NSZombieEnabled环境变量
在xCode4中的设置方法为 Product->EditScheme->Run->Environment Variables, 添加NSZombieEnabled环境变量并设为YES。如图所示。
当设置NSZombieEnabled为YES后,运行程序,此时程序不再crash,而是在向已释放对象发送消息的位置断住,并在调试器中打印中引起异常对象的消息。如图所示
2012-05-29 22:47:10.911 ZombieDebug[828:f803] *** -[CFNumber respondsToSelector:]: message sent to deallocated instance 0x687c4e0
即向CFNumber对象发送respondsToSelector消息时检测到异常,0x687c4e0地址的CFNumber对象已经被释放。
注:因启动NSZombie后,本应释放的内存变成了NSZombie而不被释放,会使程序占用的内存越来越多,所以只能在调试时设置NSZombieEnabled环境变量,在发布时要去除。
使用NSZombieEnabled环境变量的方法只能知道哪个对象出了问题,而不知道该对象的分配释放流程,所以很难分析在哪一步出了问题,使用Instruments的NSZombie检测可以完整的跟踪对象的retain,release流程,从而分析哪一次释放为异常释放。
2.3 方法2-使用Instruments的NSZombie检测
使用菜单Product->Profile可以启动Instruments,如图所示,选择Zombies模板。
Zombies模板是Allocations模板的一个特例,其启用了Record Reference Counts和Enable NSZombie detection。
当Instruments中点击record运行程序,当有向NSZombie发送消息时,会有消息提醒。如图
点击图中的箭头后,就显示出此对象所有的retain,release,autorelease过程,便于分析哪里进行了不正常的释放。