iOS -- Crash处理方法总结
来源:互联网 发布:淘宝联盟文案生成器 编辑:程序博客网 时间:2024/06/08 12:06
在调试程序的时候,总是碰到crash的bug,而且一追踪就是一些汇编的代码,让人特别疑惑。下面总结一些crash调试几种的方法。
基本上有错误分为以下几种类型:
signal(SIGABRT, MySignalHandler);
signal(SIGILL, MySignalHandler);
signal(SIGSEGV, MySignalHandler);
signal(SIGFPE, MySignalHandler);
signal(SIGBUS, MySignalHandler);
signal(SIGPIPE, MySignalHandler);
SIGABRT和EXC_BAD_ACCESS较为特殊,算是比较好跟进。
SIGABRT是系统报错,在memery warning之后,系统会把程序强制退出,报的就是这个错误。
EXC_BAD_ACCESS 大多数时候是内存提前释放而引起的问题,或者访问的方法不存在引起的。
SIGSEGV 使用全局断点解决。
追踪程序的调用stacktrace的方法,跟踪问题:
一般出错了之后出现的常见界面,再熟悉不过了。。
切换到breakpoint界面,拖动底端的slider按钮,显示调用堆栈
虽然调用堆栈已经出来了,但是都是一堆的汇编代码,无法给予明确的信息。。
设置全局异常断点(SIGSEGV )
有时候我们看不懂堆栈里的汇编代码,最后能够定位到出错的地方。那就用到下面的内容了:添加Symbol breakpoint Exception breakpoint
一、Exception breakpoint全局断点。
在程序抛出异常时候,往往需要定位到异常,添加Add Exception BreakPoint,这个就是捕获所有的exception, 貌似stackoverflow上说,bad_access那种错误无法捕获的,这个用于捕获那些SIGSEGV 的错误。
二、Symbolic breakpoint
系统抛出异常处设置断点
有时候我们的程序不知道跑到哪个地方就 crash 了,而 crash 又很难重现。保守的做法是在系统抛出异常之前设置断点,具体来说是在 objc_exception_throw处设置断点。 这样在 Debug 模式下,如果程序即将抛出异常,就能在抛出异常处中断了。
添加完成之后在 Symbol 一栏输入:objc_exception_throw,然后点击 done,完成。
添加完成只两个断点之后,程序中很多异常也可以捕获了,直接定位到出问题的位置。
开启僵尸模式(EXC_BAD_ACCESS)
1、为什么会使用NSZombieEnabled?
应用调试可能会收到类似 Thread
当设置NSZombieEnabled环境变量后,一个对象销毁时会被转化为_NSZombie,设置NSZombieEnabled后,当你向一个已经释放的对象发送消息,这个对象就不会向之前那样Crash或者产生一个难以理解的行为,而是放出一个错误消息,然后以一种可预测的可以产生debug断点的方式消失, 因此我们就可以找到具体或者大概是哪个对象被错误的释放了。
2、如何设置为NSZombieEnabled模式?
Xcode4 下设置 NSZombieEnabled 的方法:
方法一:Product -> Edit Scheme-> Arguments, 然后将点击”加号”, 将 NSZombieEnabled 参数加到Environment Variables 窗口中, 后面的数值写上 ”YES”.
方法二:Xcode4 菜单 Product -> EditScheme -> Diagnostics 设置窗口中直接勾上Enable ZombieObjects 即可
Xcode 可用 cmd+shift+< 进到这个窗口。
3、一个简单实例
static NSMutableArray*array;- (void)viewWillAppear:(BOOL)animated{
}
-(void)viewDidLoad
{
}
上例的运行结果:在未开启NSZombieEnabled的状态下,会输出Hello,程序在调用之viewWillAppear的时候crash,开启NSZombieEnabled的情况下,不会输出Hello,在调用Hello的情况下应用即会停止运行,控制台报错:
*** -[__NSArrayM respondsToSelector:]: message sent to deallocated instance 0x6aa0200
4、一个复杂一点的实例
#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问题。
5、有什么需要注意的?
NSZombieEnabled只能在调试的时候使用,千万不要忘记在产品发布的时候去掉,因为NSZombieEnabled不会真正去释放dealloc对象的内存,一直开启后果可想而知
Xcode内置调试命令:GDB
(lldb) po [$eax class]:输出异常对象的地址1.2 print 命令:有点类似于格式化输出,可以输出对象的不同信息
(lldb) po [$eax name]:输出这个异常的名字
(lldb) po [$eax reason]:这个将会输出错误消息:
(lldb) “po $eax”:对这个对象调用“description”方法和打印出来“$eax”是cup的一个寄存器。在一个异常的情况下,这个寄存器将会包含一个异常对象的指针。注意:$eax只会在模拟器里面工作,假如你在设备上调试,你将需要使用”$r0″寄存器
比如:print (char*)[[dic description] cString]、(lldb) print (int)[label retainCount]
1.3 info 命令:我们可以查看内存地址所在信息
1.4 info line *内存地址:可以获取内存地址所在的代码行相关信息
1.5 show 命令:显示 GDB 相关的信息。如:show version 显示GDB版本信息
1.6 bt: 显示当前进程的函数调用栈的情况;"up num":查看调用的详细信息;down:返回栈列表;l:显示详细代码信息;p:输出数值。
打印对象生命周期日志
NSLog
- (id)init
{self = [super init];
if (self){NSLog(@"%@: %@", NSStringFromSelector(_cmd), self);}
return self;
}
- (void)dealloc
{NSLog(@"%@: %@", NSStringFromSelector(_cmd), self);
}
动态特性
-(BOOL)respondsToSelector:(SEL)aSelector
{
if ([super respondsToSelector:aSelector] ){
NSLog(@"%@", NSStringFromSelector(aSelector));
return YES;
}
else {
return NO;
}
}
OC的异常处理机制Exception
Object-C语言的异常处理符号和C++、JAVA相似。再加上使用NSException,NSError或者自定义的类,你可以在你的应用程序里添加强大的错误处理机制。
异常处理机制是由这个四个关键字支持的:@try,@catch,@thorw,@finally。当代码有可能出现异常时,我们把他放到@try语句块中。@catch()块包含了处理@try块里的抛出的异常的逻辑。无论异常是否发生,@finally块里面的语句都会执行。如果直接使用@throw块来抛出异常,这个异常本质上是一个OC的对象。咱们可以使用NSException对象,但是不局限于他们。
注意:要在一个应用里同时使用这些特性,应用必须在MAC OS X v10.3或者以上版本中。因为早期版本的运行环境不支持异常处理和线程同步。
举个简单的例子:
一公分为两部分一部分是注册异常,一部分是在需要的情况下抛出异常
抛出异常
为了掷出一个异常,我们必须实例化一个对象,当然这个异常对象要包含相关的信息,比如异常的名字和为什么要掷出他。
和@catch()块相反,你可以使用@throw再次掷出一个被抓到的异常,不用加参数哦亲。这个能使你的代码更可读。(我怎么没看出来)
你也可以继承NSEception,来实现特殊类型的异常,比如文件系统的异常或者交互异常。
注意:不用仅限于掷出NSException对象。你可以掷出任何一个OC对象作为一场对象。NSException类提供的方法可以帮助你处理异常,但是如果你愿意你可以实现你自己的。
高端处理异常(尼玛这标题怎么翻啊,来去就是这几个单词,老外哥哥你敢不敢换几个啊)
为了捕获一个使用@try块掷出的异常,你可以在@try后面使用多个@catch()块,可以用多个哦亲。@catch块可以由最特殊的到最普遍的排序(我觉得翻译成重要不重要的比普遍好多了,但是尊重GOOGLE,你们懂的)。这样你就可以度身订造你的异常处理过程。
如果这还不够透彻,那么我们就干脆我们自定义两个异常类,看看异常异常处理的使用。
1、新建SomethingException,SomeOverException这两个类,都继承与NSException类。
- #import
<Foundation/Foundation.h> -
- @interface
SomethingException : NSException -
- @end
- #import
"SomethingException.h" -
- @implementation
SomethingException -
- @end
- #import
<Foundation/Foundation.h> -
- @interface
SomeOverException : NSException -
- @end
- #import
"SomeOverException.h" -
- @implementation
SomeOverException -
- @end
2、新建Box类,在某些条件下产生异常。
- #import
<Foundation/Foundation.h> -
- @interface
Box : NSObject - {
-
NSInteger number; - }
- -(void)
setNumber: (NSInteger) num; - -(void)
pushIn; - -(void)
pullOut; - -(void)
printNumber; - @end
- @implementation
Box - -(id)
init { -
self = [super init]; -
-
if ( self ) { -
[self setNumber: 0]; -
} -
-
return self; - }
-
- -(void)
setNumber: (NSInteger) num { -
number = num; -
-
if ( number > 10 ) { -
NSException *e = [SomeOverException -
exceptionWithName: @"BoxOverflowException" -
reason: @"The level is above 100" -
userInfo: nil]; -
@throw e; -
} else if ( number >= 6 ) { -
// throw warning -
NSException *e = [SomethingException -
exceptionWithName: @"BoxWarningException" -
reason: @"The level is above or at 60" -
userInfo: nil]; -
@throw e; -
} else if ( number < 0 ) { -
// throw exception -
NSException *e = [NSException -
exceptionWithName: @"BoxUnderflowException" -
reason: @"The level is below 0" -
userInfo: nil]; -
@throw e; -
} - }
-
- -(void)
pushIn { -
[self setNumber: number + 1]; - }
-
- -(void)
pullOut { -
[self setNumber: number - 1]; - }
-
- -(void)
printNumber { -
NSLog(@"Box number ,is: %d" number); - }
- @end
这里写 [SomeOverException
3、使用Box,在适当添加下捕获Box类的异常
3.1、在没超过6时,没有异常
- -
( void)viewDidLoad - {
-
[super viewDidLoad]; -
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; -
Box *box = [[Box alloc]init]; -
for ( inti = 0; i < 5; i++) { -
[box pushIn]; -
[box printNumber]; -
} - }
Box number is: 1
Box number is: 2
Box number is: 3
Box number is: 4
Box number is: 5
3.2 超过6,产生异常
- for
( inti = 0; i < 11; i++) { -
[box pushIn]; -
[box printNumber]; -
}
- 2012-07-04
09:12:05.889 ObjectiveCTest[648:f803] Box number is: 1 - 2012-07-04
09:12:05.890 ObjectiveCTest[648:f803] Box number is: 2 - 2012-07-04
09:12:05.890 ObjectiveCTest[648:f803] Box number is: 3 - 2012-07-04
09:12:05.890 ObjectiveCTest[648:f803] Box number is: 4 - 2012-07-04
09:12:05.891 ObjectiveCTest[648:f803] Box number is: 5 - 2012-07-04
09:12:05.891 ObjectiveCTest[648:f803] *** Terminating app due to uncaught exception 'BoxWarningException', reason: 'Thenumber is above or at 60'
3.3、加上异常处理
- for
( inti = 0; i < 11; i++) { -
@try { -
[box pushIn]; -
} -
@catch (SomethingException *exception) { -
NSLog(@"%@ %@" ,[exception name], [exception reason]); -
} -
@catch (SomeOverException *exception) { -
NSLog(@"%@", [exception name]); -
} -
@finally { -
[box printNumber]; -
} -
}
- 2012-07-04
09:14:35.165 ObjectiveCTest[688:f803] Box number is: 1 - 2012-07-04
09:14:35.167 ObjectiveCTest[688:f803] Box number is: 2 - 2012-07-04
09:14:35.167 ObjectiveCTest[688:f803] Box number is: 3 - 2012-07-04
09:14:35.167 ObjectiveCTest[688:f803] Box number is: 4 - 2012-07-04
09:14:35.167 ObjectiveCTest[688:f803] Box number is: 5 - 2012-07-04
09:14:35.167 ObjectiveCTest[688:f803] BoxWarningException The number is above or at 60 - 2012-07-04
09:14:35.168 ObjectiveCTest[688:f803] Box number is: 6 - 2012-07-04
09:14:35.168 ObjectiveCTest[688:f803] BoxWarningException The number is above or at 60 - 2012-07-04
09:14:35.168 ObjectiveCTest[688:f803] Box number is: 7 - 2012-07-04
09:14:35.168 ObjectiveCTest[688:f803] BoxWarningException The number is above or at 60 - 2012-07-04
09:14:35.168 ObjectiveCTest[688:f803] Box number is: 8 - 2012-07-04
09:14:35.168 ObjectiveCTest[688:f803] BoxWarningException The number is above or at 60 - 2012-07-04
09:14:35.169 ObjectiveCTest[688:f803] Box number is: 9 - 2012-07-04
09:14:35.169 ObjectiveCTest[688:f803] BoxWarningException The number is above or at 60 - 2012-07-04
09:14:35.169 ObjectiveCTest[688:f803] Box number is: 10 - 2012-07-04
09:14:35.169 ObjectiveCTest[688:f803] BoxOverflowException - 2012-07-04
09:14:35.225 ObjectiveCTest[688:f803] Box number is: 11
3.4 、小于0时的异常
- @try
{ -
[box setNumber:-10]; -
} -
@catch (NSException *exception) { -
NSLog(@"%@",[exception name]); -
} -
@finally { -
[box printNumber]; -
}
- 2012-07-04
09:17:42.405 ObjectiveCTest[753:f803] BoxUnderflowException - 2012-07-04
09:17:42.406 ObjectiveCTest[753:f803] Box number is: -10
参考链接:http://www.dkankan.com/archives/7221
参考链接:http://blog.sina.com.cn/s/blog_71715bf8010166qf.html
over
- iOS -- Crash处理方法总结
- IOS crash处理
- IOS Parse 处理crash
- iOS Crash 处理办法
- IOS crash处理
- iOS crash 总结
- ios crash分析方法
- iOS 调试 crash breakpoint EXC_BAD_ACCESS SIGABRT方法总结
- iOS crash日志的处理
- iOS Crash 崩溃日志总结
- ios各种异常crash总结
- Android Crash异常处理方法
- iOS截取crash log方法
- IOS 系统crash分析方法
- ios 符号化 crash的方法
- ios中crash检测方法
- 处理、分析iOS App的Crash Reports
- iOS 数组越界 Crash处理经验
- [v9] phpcms V9 去除内容页里面的div字符串,防止网页变形
- jQuery deferred 对象的 promise 方法
- Android 多线程处理之多线程用法大集合
- iOS手势识别的详细使用(拖动,缩放,旋转,点击,手势依赖,自定义手势)
- SolrCloud Wiki翻译(3)Shards & Indexing Data
- iOS -- Crash处理方法总结
- android模块化app开发-3远程动态更新插件
- Judy矩阵--Judy.h文件
- shimmer/空气干扰/空间扭曲
- 苹果新的编程语言 Swift 语言进阶(十五)--协议
- poj1477 Box of Bricks
- 自动化测试 - 单元测试
- NFS客户端、服务器协商读写粒度(rsize、wsize)流程
- ios 获取当前wifi名称