iphone调试相关问题

iphone ios XCode4如何调试程序忽然崩溃而找不到挂的代码:(设置环境参数查内存泄露)

XCode4设置不太一样: 在Edit–>Scheme里面 找到Arguments(在xcode 4.2中,在菜单 product->edit scheme中)


把下面3个值设置成YES
NSAutoreleaseFreedObjectCheckEnabled
NSZombieEnabled
NSDebugEnabled

NSZombieEnabled

MallocStackLogging (来启用malloc记录)

MallocStackLoggingNoCompact

NAME OF ENV. VARIABLE               DEFAULT    SET TO... NSDebugEnabled                        NO       "YES" NSZombieEnabled                       NO       "YES" NSDeallocateZombies                   NO       "YES" NSHangOnUncaughtException             NO       "YES" NSEnableAutoreleasePool              YES       "NO" NSAutoreleaseFreedObjectCheckEnabled  NO       "YES" NSAutoreleaseHighWaterMark             0       non-negative integer NSAutoreleaseHighWaterResolution       0       non-negative integer

当问题解决后,要将NSZombieEnabled设置为无效,不需要删除变量,将变量前的对号去掉就可以了


其他相关问题:

EXC_BAD_ACCESS:向已经释放的对象发送消息时会出现EXC_BAD_ACCESS。(这个错误的90%来源在于对一个已经释放的对象进行release操作)

如果你设置了NSZombiEnabled环境变量,当销毁一个对象时,objective的运行时环境会在这个对象后边设置一个dummy,当 调用这个对象的方法时,程序会终止,并在堆栈上显示错误信息。 NSZombiEnabled=YES, 这个参数值可以让GDB在发现使用已经释放的对象时给出一个有用信息

发现错误定位到main函数,main 函数里有个内存池来维护使用自动回收机制的对象,错误定位到main 可能就是这个位置出了问题(哪个地方释放过度)

can recommend calling [NSAutoreleasePool showPools] to get a log of the current pool levels and contents


如果有如下一段代码

  
  
  
  
  1. //重复释放一个变量   
  2.  NSAutoreleasePoolpool = [[NSAutoreleasePool alloc] init];  
  3.  NSDatadata = [NSData dataWithBytes:"asklaskdxjgr" length:12];  
  4.  [data release];  
  5.  [pool release]; 

  再Debug窗口会有如下的提示

  
  
  
  
  1. 2003-03-18 13:01:38.644 autoreleasebug[3939] *** *** Selector release  
  2. ent to dealloced instance 0xa4e10 of class NSConcreteData. 

  虽然也能大致判断是哪种类型的变量重复释放了,但信息还不够多,当项目大,源码比较多的时候,也不太方便定位

  在console窗口运行如下命令可以得到更多信息:

  
  
  
  
  1. shell malloc_history <pid> <address>

  输入命令:

  
  
  
  
  1. shell malloc_history 3939 0xa4e10 

  就会出现更多的信息:

  
  
  
  
  1. [dave@host193 Frameworks]$ malloc_history 3939 0xa4e10  
  2. Call [2] [arg=32]: thread_a0000dec |0x1000 | start | _start | main |  
  3. +[NSData dataWithBytes:length:] | NSAllocateObject | object_getIndexedIvars |  
  4. malloc_zone_calloc   

  这个时候就知道具体哪个函数出先问题了,从这里可以看到main里NSData出现问题了  。

  2、NSArray等集合类的实用问题  。

  如下面的代码   

  
  
  
  
  1. ReleaseTest* rt = [[alloc] init];  
  2. NSMutableArray *array = [[NSMutableArray alloc] init] ;  
  3. [array addObject: rt];  
  4. ReleaseTest *rt2 = [array objectAtIndex:0];  
  5.     [rt2 release];     
  6. [array release];  
  7. [rt release];  

  就会导致重复释放内存问题,因为rt2,获取的是一个对象的指针,如果已经释放了的话,rt在释放的话,就重复了,为了遵循谁Init谁Release的原则,rt2就不应该Release  。

  3、init 和 Release 的问题  。

  凡是对象通过Init的方法生成的对象,都需要自己负责Release  。

  凡不是通过Init的方法生成的对象,不需要负责Release.如[NSString StringWithFormat]方法生成的对象就不需要自己Release,因此自己定义函数,返回一个类的时候,一定要设置为autoRelease   。这样调用的人就不用操心是否需要释放对象了  。

  4、AutoReleasePool里最好不要将AutoRelease对象赋值给其它对象,否则离开这个作用域后,对象将会Release掉  。



例子:

#import <Foundation/Foundation.h>

int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
        NSString* s = [[NSString alloc]initWithString:@”This is a test string”];
        s = [s substringFromIndex:[s rangeOfString:@"a"].location];//内存泄露
        [s release];//错误释放
[pool drain];//EXC_BAD_ACCESS
return 0;
}


    这个例子当然狠容易的看出问题所在,如果这段代码包含在一个很大的逻辑中,确实容易被忽略。Objective-C 这段代码有三个致命问题:1、内存泄露;2、错误释放;3、造成 EXC_BAD_ACCESS 错误。

    1,内存泄露。 NSString* s = [[NSString alloc]initWithString:@”This is a test string”]; 创建了一个 NSString Object,随后的 s = [s substringFromIndex:[s rangeOfString:@"a"].location]; 执行后,导致创建的对象引用消失,直接造成内存泄露。

    2,错误释放。[s release]; 这个问题,原因之一是一个逻辑错误,以为 s 还是我们最初创建的那个 NSString 对象。第二是因为从 substringFromIndex:(NSUInteger i) 这个方法返回的 NSString 对象,并不需要我们来释放,它其实是一个被 substringFromIndex 方法标记为 autorelease 的对象。如果我们强行的释放了它,那么会造成 EXC_BAD_ACCESS 问题。

    3, EXC_BAD_ACCESS。由于 s 指向的 NSString 对象被标记为 autorelease, 则在 NSAutoreleasePool 中已有记录。但是由于我们在前面错误的释放了该对象,则当 [pool drain] 的时候,NSAutoreleasePool 又一次的对它记录的 s 对象调用了 release 方法,但这个时候 s 已经被释放不复存在,则直接导致了 EXC_BAD_ACCESS问题。



=======Xcode4 的错误调试定位技巧=========================================================================

论坛里经常有朋友会问Crash的问题。Crash最多的无非就两种,一种就是signal SIGABRT,大概的意思就是发送Message出现问题,信号迷失了。


这种的Crash其实是很好定位,Crash了后直接看Console里出的最后日志,比如这段:


2012-03-28 19:26:33.055 TableViewMenuDemo[3916:f803] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSArrayI replaceObjectAtIndex:withObject:]: unrecognized selector sent to instance 0x6a3f3d0'


*** First throw call stack:


找到reason字段,那就是原因,说NSArray 调用 replaceObjectAtIndex:withObject:


这个方法是NSMutableArry的,NSArray并没有该方法。信号迷失掉了,所以Crash了。0x6a3f3d0 是出问题的内存地址,查下内存地址或搜下调用方法就比较好定位了。



Xcode4上还有个更好用的定位方法,就是设置一个Exception Breakpoint就好了


 

 


看图在工程的左边栏中选择Breakpoint Navigator ,点击下面的+号添加一个Exception Breakpoint,然后再运行试试,Crash后,是不是直接定位到了那行代码了?再根据Console里的日志进行定位修改就好了。


下面说说另一种Crash,EXC_BAD_ACCESS ,这个比较头疼,因为Crash的时候,可能是比较早之前的某个变量释放了,现在访问时出问题。Console里也没显示什么日志。


这里光加入Exception Breakpoint是不够的了,XCode4还个可爱的地方,打开Scheme选项选择Edit  Scheme



 


 


然后按图勾上Enable Zombie Objects 和Malloc Stack那两项,记住一般只有在定位EXC_BAD_ACCESS时候才勾选,别有事没事都勾上。


这样重新跑一下,如果是到Exception Breakpoint处停止了,可以在Console中输入:c(continue)按回车继续跑,直到Crash。看下Console是不是有跟SIGABRT类似的错误信息日志了,后面定位什么的你懂的。


如果还没有日志,在Console中输入 po $eax   $eax标志出错的地方,适用模拟器,真机用$r0(话说EXC_BAD_ACCESS这种错误模拟器定位就行),还可以输入比如:po [$eax name]     po [$eax reason]等指令查看错误其他信息(注意方括号后没分号的)。然后,就没有然后了。


还要补充点,程序开发过程就要多关注左边栏中Issue Navigator里的警告信息,Xcode4不仅会警告,还多数给出解决建议。能避免后面不必要的Crash




 


如果程序Crash,第一时间关注下debug Navigator里的执行信息,将滑块拉向右边可以看到更多调用信息,根据这个能大致设想是调用什么方法或进行什么操作时Crash的。


 


好了,大致这些就能比较有效率的解决一般Crash问题。欢迎大家有独到的经验可以补充进来。


你可能感兴趣的:(xcode,Scheme,iPhone,Access,XCode4,frameworks)