demo地址:https://github.com/easonoutlook/UncaughtExceptionHandler

IOS SDK中提供了一个现成的函数 NSSetUncaughtExceptionHandler 用来做异常处理,但功能非常有限,而引起崩溃的大多数原因如:内存访问错误,重复释放等错误就无能为力了,因为这种错误它抛出的是Signal,所以必须要专门做Signal处理。首先定义一个UncaughtExceptionHandler类,.h头文件的代码如下:

#import <UIKit/UIKit.h>

@interface UncaughtExceptionHandler : NSObject

{

BOOL dismissed;

}

@end

void InstallUncaughtExceptionHandler();

然后在.mm文件实现InstallUncaughtExceptionHandler(),如下:

void InstallUncaughtExceptionHandler()

{

signal(SIGABRT, MySignalHandler);

signal(SIGILL, MySignalHandler);

signal(SIGSEGV, MySignalHandler);

signal(SIGFPE, MySignalHandler);

signal(SIGBUS, MySignalHandler);

signal(SIGPIPE, MySignalHandler);

}

这样,当应用发生错误而产生上述Signal后,就将会进入我们自定义的回调函数MySignalHandler。为了得到崩溃时的现场信息,还可以加入一些获取CallTrace及设备信息的代码,.mm文件的完整代码如下:

#import "UncaughtExceptionHandler.h"

#include <libkern/OSAtomic.h>

#include <execinfo.h>

NSString * const UncaughtExceptionHandlerSignalExceptionName = @"UncaughtExceptionHandlerSignalExceptionName";

NSString * const UncaughtExceptionHandlerSignalKey = @"UncaughtExceptionHandlerSignalKey";

NSString * const UncaughtExceptionHandlerAddressesKey = @"UncaughtExceptionHandlerAddressesKey";

volatile int32_t UncaughtExceptionCount = 0;

const int32_t UncaughtExceptionMaximum = 10;

const NSInteger UncaughtExceptionHandlerSkipAddressCount = 4;

const NSInteger UncaughtExceptionHandlerReportAddressCount = 5;

@implementation UncaughtExceptionHandler

+ (NSArray *)backtrace

{

        void* callstack[128];

 int frames = backtrace(callstack, 128);

 char **strs = backtrace_symbols(callstack, frames); 

 int i;

 NSMutableArray *backtrace = [NSMutableArray arrayWithCapacity:frames];

 for (

 i = UncaughtExceptionHandlerSkipAddressCount;

 i < UncaughtExceptionHandlerSkipAddressCount +

UncaughtExceptionHandlerReportAddressCount;

i++)

 {

 [backtrace addObject:[NSString stringWithUTF8String:strs[i]]];

 }

 free(strs); 

 return backtrace;

}

- (void)alertView:(UIAlertView *)anAlertView clickedButtonAtIndex:(NSInteger)anIndex

{

if (anIndex == 0)

{

dismissed = YES;

}

}

- (void)handleException:(NSException *)exception

{

UIAlertView *alert =

[[[UIAlertView alloc]

initWithTitle:NSLocalizedString(@"Unhandled exception", nil)

message:[NSString stringWithFormat:NSLocalizedString(

@"You can try to continue but the application may be unstable.\n"

@"%@\n%@", nil),

[exception reason],

[[exception userInfo] objectForKey:UncaughtExceptionHandlerAddressesKey]]

delegate:self

cancelButtonTitle:NSLocalizedString(@"Quit", nil)

otherButtonTitles:NSLocalizedString(@"Continue", nil), nil]

autorelease];

[alert show];

CFRunLoopRef runLoop = CFRunLoopGetCurrent();

CFArrayRef allModes = CFRunLoopCopyAllModes(runLoop);

while (!dismissed)

{

for (NSString *mode in (NSArray *)allModes)

{

CFRunLoopRunInMode((CFStringRef)mode, 0.001, false);

}

}

CFRelease(allModes);

NSSetUncaughtExceptionHandler(NULL);

signal(SIGABRT, SIG_DFL);

signal(SIGILL, SIG_DFL);

signal(SIGSEGV, SIG_DFL);

signal(SIGFPE, SIG_DFL);

signal(SIGBUS, SIG_DFL);

signal(SIGPIPE, SIG_DFL);

if ([[exception name] isEqual:UncaughtExceptionHandlerSignalExceptionName])

{

kill(getpid(), [[[exception userInfo] objectForKey:UncaughtExceptionHandlerSignalKey] intValue]);

}

else

{

[exception raise];

}

}

@end

NSString* getAppInfo()

{

    NSString *appInfo = [NSString stringWithFormat:@"App : %@ %@(%@)\nDevice : %@\nOS Version : %@ %@\nUDID : %@\n",

                          [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"],

                          [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"],

                          [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"],

                          [UIDevice currentDevice].model,

                          [UIDevice currentDevice].systemName,

                          [UIDevice currentDevice].systemVersion,

                          [UIDevice currentDevice].uniqueIdentifier];

    NSLog(@"Crash!!!! %@", appInfo);

    return appInfo;

}

void MySignalHandler(int signal)

{

int32_t exceptionCount = OSAtomicIncrement32(&UncaughtExceptionCount);

if (exceptionCount > UncaughtExceptionMaximum)

{

return;

}

NSMutableDictionary *userInfo =

[NSMutableDictionary

dictionaryWithObject:[NSNumber numberWithInt:signal]

forKey:UncaughtExceptionHandlerSignalKey];

NSArray *callStack = [UncaughtExceptionHandler backtrace];

[userInfo

setObject:callStack

forKey:UncaughtExceptionHandlerAddressesKey];

[[[[UncaughtExceptionHandler alloc] init] autorelease]

performSelectorOnMainThread:@selector(handleException:)

withObject:

[NSException

exceptionWithName:UncaughtExceptionHandlerSignalExceptionName

reason:

[NSString stringWithFormat:

NSLocalizedString(@"Signal %d was raised.\n"

                                          @"%@", nil),

signal, getAppInfo()]

userInfo:

[NSDictionary

dictionaryWithObject:[NSNumber numberWithInt:signal]

forKey:UncaughtExceptionHandlerSignalKey]]

waitUntilDone:YES];

}

void InstallUncaughtExceptionHandler()

{

signal(SIGABRT, MySignalHandler);

signal(SIGILL, MySignalHandler);

signal(SIGSEGV, MySignalHandler);

signal(SIGFPE, MySignalHandler);

signal(SIGBUS, MySignalHandler);

signal(SIGPIPE, MySignalHandler);

}

在应用自身的 didFinishLaunchingWithOptions 前,加入一个函数:

- (void)installUncaughtExceptionHandler

{

InstallUncaughtExceptionHandler();

}

最后,在 didFinishLaunchingWithOptions 中加入这一句代码就行了:

[self InstallUncaughtExceptionHandler];

现在,基本上所有崩溃都能Hold住了。崩溃时将会显示出如下的对话框:

这样在崩溃时还能从容地弹出对话框,比起闪退来,用户也不会觉得那么不爽。然后在下次启动时还可以通过邮件来发送Crash文件到邮箱,这就看各个应用的需求了。

转载于:https://www.cnblogs.com/easonoutlook/archive/2012/12/27/2835979.html

iOS捕获异常的处理相关推荐

  1. ios - 使用@try、catch捕获异常:

    @try {// 可能会出现崩溃的代码 } @catch (NSException *exception) {// 捕获到的异常exception } @finally {// 结果处理 } 转载于: ...

  2. iOS 中捕获程序崩溃日志

    iOS开发中遇到程序崩溃是很正常的事情,如何在程序崩溃时捕获到异常信息并通知开发者,是大多数软件都选择的方法.下面就介绍如何在iOS中实现: 1. 在程序启动时加上一个异常捕获监听,用来处理程序崩溃时 ...

  3. ios html 调试,使用iframe和vconsole调试ios网页

    首先在低版本的ios浏览器, 即使开启调试也因为电脑端版本太新, 无法查看webview或者浏览器中的log, 真的是垃圾 下载 开发者版本的Safari也不行 只能使用一个壳子, 将目标网页放到if ...

  4. ios nstimer实现延时_iOS 中常见 Crash 总结

    作者 | 在路上重名了啊 @(iOS总结)[温故而知新] [TOC] 1.找不到方法的实现unrecognized selector sent to instance 2.KVC造成的crash 3. ...

  5. 经营你的iOS应用日志(二):异常日志

    http://www.cnblogs.com/alario/archive/2012/03/28/2421574.html#2343515 如果你去4S店修车,给小工说你的车哪天怎么样怎么样了,小工有 ...

  6. 漫谈iOS Crash收集框架

    为了能够第一时间发现程序问题,应用程序需要实现自己的崩溃日志收集服务,成熟的开源项目很多,如 KSCrash,plcrashreporter,CrashKit 等.追求方便省心,对于保密性要求不高的程 ...

  7. iOS应用日志:开始编写日志组件与异常日志

    应用日志(一):开始编写日志组件 对于那些做后端开发的工程师来说,看 LOG解Bug应该是理所当然的事,但我接触到的移动应用开发的工程师里面,很多人并没有这个意识,查Bug时总是一遍一遍的试图重现,试 ...

  8. iOS 开发:『Runtime』详解(二)Method Swizzling

    本文用来介绍 iOS 开发中『Runtime』中的黑魔法Method Swizzling. 通过本文,您将了解到: Method Swizzling(动态方法交换)简介 Method Swizzlin ...

  9. ①(语法篇)、《史上最全iOS八股文面试题》2022年,金三银四我为你准备了,iOS《1000条》笔试题以及面试题(包含答案)。带面试你过关斩将,(赶紧过来背iOS八股文)

    iOS面试题 一共分为笔试题和面试题两部分 笔试题 一共分为10个 总共613题 面试题 一共400题 笔试题 一个10个系列 分别为 ①(语法篇) 共147题 已更新 ②(常识篇) 共72题 已更新 ...

最新文章

  1. Java学习之生成随机数
  2. 全球首个塑料ARM芯片登上Nature,成本仅同类硅芯片1/10
  3. Go指南练习_rot13Reader
  4. Word2vec 讨论
  5. jQuery停止动画
  6. android动态获取输入值,Android实现动态自动匹配输入内容
  7. php控制器教程,laravel基础教程 -- 控制器
  8. 硬件芯片----74HC595芯片的运用原理
  9. 【道高一尺,魔高一丈】Python爬虫之如何应对网站反爬虫策略
  10. 用jquery1.9版本判断ie浏览器及ie6浏览器
  11. 电脑软件测试英雄联盟,揭秘英雄联盟的自动化测试
  12. python飞机大战爆炸效果实现_python飞机大战添加爆炸效果
  13. mplfinance 一个堪称完美python量化金融可视化工具详析
  14. live.com与live.cn邮箱收不到QQ绑定激活信的解决方法
  15. supervisor查询状态报错
  16. linux 启动 参数,Linux启动参数
  17. pycurl和urllib2的比较
  18. 配置路由器接口使用PPP协议封装
  19. Ext2explore查看ext2/ext3/ext4 file
  20. 软件工程经济学复习笔记

热门文章

  1. 存储过程中while循环
  2. ES6——函数的name属性
  3. hp android 平板,惠普三款Android平板电脑新产品曝光
  4. 眨眼快奔四了,大家的存款都有多少啊?
  5. 你遇到过最有诗意的句子是什么?
  6. 你还记得珠算的口诀吗?
  7. 坐异性朋友的车时,能坐在副驾驶吗?
  8. 广西桂林平均工资是多少?
  9. 初步看,我要在公司赚取更多的工资,钱多活少离家近
  10. 今天来谈一谈环境对我们的习惯有什么影响