IOS应用之一--异常处理(UncaughtExceptionHandler)

使用原因

iOS开发中我们会遇到程序抛出异常退出的情况,如果是在调试的过程中,异常的信息是一目了然,但是如果是在已经发布的程序中,获取异常的信息有时候是比较困难的。

好处与缺点

iOS提供了异常发生的处理API,我们在程序启动的时候可以添加这样的Handler,这样的程序发生异常的时候就可以对这一部分的信息进行必要的处理,适时的反馈给开发者。

不足的地方是,并不是所有的程序崩溃都是由于发生可以捕捉的异常的,有些时候是因为内存等一些其他的错误导致程序的崩溃,这样的信息是不在这里体现的。

常用的处理方式

第一种方式:作基本的操作,可以添加和获取Handler,捕获到异常后将信息写入到app的Documens下的Exception.txt中。

第二种方式:比如可以在程序下一次起来的时候读取这个异常文件发生到服务端

第三种方式:或者直接就是在处理代码中用openurl的方式(mailto:)调用发送邮件的方式,将异常信息直接变成邮件发送到指定地址,其实还有很多的处理的办法。

实例 :

#pragma mark  代理类中的写法

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

[window makeKeyAndVisible];
[NdUncaughtExceptionHandler setDefaultHandler];
NSArray *array = [NSArray arrayWithObject:@"there is only one objective in this arary,call index one, app will crash and throw an exception!"];
NSLog(@"%@", [array objectAtIndex:1]);

return YES;
}

异常基本接口展示:

#import <Foundation/Foundation.h>

@interface NdUncaughtExceptionHandler : NSObject {

}

+ (void)setDefaultHandler;
+ (NSUncaughtExceptionHandler*)getHandler;

@end
//还可以选择设置自定义的handler,让用户取选择

接口实现展示
#import "NdUncaughtExceptionHandler.h"

NSString *applicationDocumentsDirectory() {
return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
}

void UncaughtExceptionHandler(NSException *exception) {
NSArray *arr = [exception callStackSymbols];
NSString *reason = [exception reason];
NSString *name = [exception name];

NSString *url = [NSString stringWithFormat:@"=============异常崩溃报告=============\nname:\n%@\nreason:\n%@\ncallStackSymbols:\n%@",
name,reason,[arr componentsJoinedByString:@"\n"]];
NSString *path = [applicationDocumentsDirectory() stringByAppendingPathComponent:@"Exception.txt"];
[url writeToFile:path atomically:YES encoding:NSUTF8StringEncoding error:nil];
//除了可以选择写到应用下的某个文件,通过后续处理将信息发送到服务器等
//还可以选择调用发送邮件的的程序,发送信息到指定的邮件地址
//或者调用某个处理程序来处理这个信息
}

@implementation NdUncaughtExceptionHandler

-(NSString *)applicationDocumentsDirectory {
return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
}

+ (void)setDefaultHandler
{
NSSetUncaughtExceptionHandler (&UncaughtExceptionHandler);
}

+ (NSUncaughtExceptionHandler*)getHandler
{
return NSGetUncaughtExceptionHandler();
}

@end

异常崩溃报告:
=============异常崩溃报告=============
name:
NSRangeException
reason:
*** -[NSArray objectAtIndex:]: index 1 beyond bounds [0 .. 0]
callStackSymbols:
0 CoreFoundation 0x02393919 __exceptionPreprocess + 185
1 libobjc.A.dylib 0x024e15de objc_exception_throw + 47
2 CoreFoundation 0x0238958c -[__NSArrayI objectAtIndex:] + 236
3 UncaughtE 0x000022e8 -[UncaughtEAppDelegate application:didFinishLaunchingWithOptions:] + 157
4 UIKit 0x002b8543 -[UIApplication _callInitializationDelegatesForURL:payload:suspended:] + 1163
5 UIKit 0x002ba9a1 -[UIApplication _runWithURL:payload:launchOrientation:statusBarStyle:statusBarHidden:] + 346
6 UIKit 0x002c4452 -[UIApplication handleEvent:withNewEvent:] + 1958
7 UIKit 0x002bd074 -[UIApplication sendEvent:] + 71
8 UIKit 0x002c1ac4 _UIApplicationHandleEvent + 7495
9 GraphicsServices 0x02bf9afa PurpleEventCallback + 1578
10 CoreFoundation 0x02374dc4 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 52
11 CoreFoundation 0x022d5737 __CFRunLoopDoSource1 + 215
12 CoreFoundation 0x022d29c3 __CFRunLoopRun + 979
13 CoreFoundation 0x022d2280 CFRunLoopRunSpecific + 208
14 CoreFoundation 0x022d21a1 CFRunLoopRunInMode + 97
15 UIKit 0x002ba226 -[UIApplication _run] + 625
16 UIKit 0x002c5b58 UIApplicationMain + 1160
17 UncaughtE 0x00002228 main + 102
18 UncaughtE 0x000021b9 start + 53

不足的地方是,并不是所有的程序崩溃都是由于发生可以捕捉的异常的,有些时候引起崩溃的大多数原因如:内存访问错误,重复释放等错误就无能为力了,因为这种错误它抛出的是Signal,所以必须要专门做Signal处理。首先定义一个UncaughtExceptionHandler类,.h头文件的代码如下:

UncaughtExceptionHandler类,.h头文件的代码如下:

1
2
3
4
5
6
#import

@interface UncaughtExceptionHandler : NSObject{
BOOL dismissed;
}
@end
1
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 #include

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])

{
<span style="white-space:pre"> </span>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 中加入这一句代码就行了:

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

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

时间: 2024-10-24 21:07:02

IOS应用之一--异常处理(UncaughtExceptionHandler)的相关文章

IOS开发之----异常处理

本文转载至 http://blog.csdn.net/chenyong05314/article/details/7906593 转载自:http://blog.sina.com.cn/s/blog_71715bf8010166qf.html 开篇大话: Object-C语言的异常处理符号和C++.JAVA相似.再加上使用NSException,NSError或者自定义的类,你可以在你的应用程序里添加强大的错误处理机制.异常处理机制是由这个四个关键字支持的:@try,@catch,@thorw,

IOS开发之--异常处理--使用try 和 catch 来捕获错误。

一个搞java的老板问我会不会try catch  我说不会 学这么久也没听周围朋友用这个 因为苹果控制台本来就可以打印异常 特此研究一下. 1.try catch:  是捕获异常代码段   特点:对代码的实时监控  占用大量资源 2.ios中很少用到try 和catch 简单的来说,Apple虽然同时提供了错误处理(NSError)和异常处理(exception)两种机制,但是Apple更加提倡开发者使用NSError来处理程序运行中可恢复的错误.而异常被推荐用来处理不可恢复的错误. 原因有几

C++学习笔记(五):高级编程:文件和流,异常处理,动态内存,命名空间

C++ 文件和流 到目前为止,我们已经使用了 iostream 标准库,它提供了 cin 和 cout 方法分别用于从标准输入读取流和向标准输出写入流. 本教程介绍如何从文件读取流和向文件写入流. 这就需要用到 C++ 中另一个标准库 fstream,它定义了三个新的数据类型: 要在 C++ 中进行文件处理,必须在 C++ 源代码文件中包含头文件 <iostream> 和 <fstream>. 打开文件 在从文件读取信息或者向文件写入信息之前,必须先打开文件.ofstream 和

【IOS】异常捕获 拒绝闪退 让应用从容的崩溃 UncaughtExceptionHandler

虽然大家都不愿意看到程序崩溃,但可能崩溃是每个应用必须面对的现实,既然崩溃已经发生,无法阻挡了,那我们就让它崩也崩得淡定点吧. IOS SDK中提供了一个现成的函数 NSSetUncaughtExceptionHandler 用来做异常处理,但功能非常有限,而引起崩溃的大多数原因如:内存访问错误,重复释放等错误就无能为力了,因为这种错误它抛出的是Signal,所以必须要专门做Signal处理.首先定义一个UncaughtExceptionHandler类,代码如下: #import <Found

IOS捕获异常,常用的异常处理方法。

前言:在开发APP时,我们通常都会需要捕获异常,防止应用程序突然的崩溃,防止给予用户不友好的一面.其实OBJECT-C的异常处理方法和JAVA的雷同,懂JAVA的朋友一看就懂.我为什么要写这篇博文呢?因为我发现百度上的介绍方法,很多都不是我想要的,而我想要的又说得不清楚,重点是大家都是直接复制别人的代码...于是不多说,大家往下看--- 以下程序已测试并通过: 设备:IOS8模拟器中 开发工具:XCode6.1 使用@try.catch捕获异常: 以下是最简单的代码写法,其中@finally可以

iOS开发随笔--iOS捕获异常、常用的异常处理方法

在开发APP时,我们通常都会需要捕获异常,防止应用程序突然的崩溃,防止给予用户不友好的体验.其实Objective-C的异常处理方法和 JAVA的雷同,懂JAVA的朋友一看就懂.我为什么要写这篇博文呢?因为我发现百度上的介绍方法,很多都不是我想要的,而我想要的又说得不清楚,重点是 大家都是直接复制别人的代码...于是不多说,大家往下看--- 原文地址:http://www.cocoachina.com/ios/20141229/10787.html 以下程序已测试并通过: 设备:iOS 8模拟器

iOS捕获异常,常用的异常处理方法

本文转载至 http://www.cocoachina.com/ios/20141229/10787.html 前言:在开发APP时,我们通常都会需要捕获异常,防止应用程序突然的崩溃,防止给予用户不友好的体验.其实Objective-C的异常处理方法和JAVA的雷同,懂JAVA的朋友一看就懂.我为什么要写这篇博文呢?因为我发现百度上的介绍方法,很多都不是我想要的,而我想要的又说得不清楚,重点是大家都是直接复制别人的代码...于是不多说,大家往下看--- 以下程序已测试并通过: 设备:iOS 8模

iOS 异常处理(-)

在开发过程中,经常要用到异常处理,防止 程序突然崩溃,在java,c++ 中有抛异常,和断言处理,那么在oc中 是怎么处理异常的呢? 1. NSAssert 看看ios 是怎么定义 #if !defined(_NSAssertBody) #define NSAssert(condition, desc, ...) do { __PRAGMA_PUSH_NO_EXTRA_ARG_WARNINGS if (!(condition)) { [[NSAssertionHandler currentHan

ios异常处理

Malformed or corrupted AST file: 'Unable to load module "/Users/topbar/Library/Developer/Xcode/DerivedData/ModuleCache/3TJWGBUKOEC41/Darwin.pcm": module file out of date' fatal error: malformed or corrupted AST file: 'Unable to load module "