使用原因

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文件到邮箱,这就看各个应用的需求了。

转载于:https://www.cnblogs.com/jwblogmj/p/4244226.html

IOS应用之一--异常处理(UncaughtExceptionHandler)相关推荐

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

    转载自:http://blog.sina.com.cn/s/blog_71715bf8010166qf.html 开篇大话: Object-C语言的异常处理符号和C++.JAVA相似.再加上使用NSE ...

  2. 数值分析 使用c语言 源码_分析源码,学会正确使用 Java 线程池

    在日常的开发工作当中,线程池往往承载着一个应用中最重要的业务逻辑,因此我们有必要更多地去关注线程池的执行情况,包括异常的处理和分析等.本文主要聚焦在如何正确使用线程池上,以及提供一些实用的建议.文中会 ...

  3. C++学习 高级编程

    C++ 文件和流 到目前为止,目前使用最为广泛的是 iostream 标准库,它提供了 cin 和 cout 方法分别用于从标准输入读取流和向标准输出写入流. 以下将介绍从文件读取流和向文件写入流.这 ...

  4. iOS-Core-Animation-Advanced-Techniques(六)

    转载自:http://www.cocoachina.com/ios/20150106/10839.html Cocos开发者平台 Cocos引擎中文官网 H5轻游戏平台 退出3471815256 iO ...

  5. iOS-Core-Animation-Advanced-Techniques(五)

    转载自:http://www.cocoachina.com/ios/20150105/10829.html Cocos开发者平台 Cocos引擎中文官网 H5轻游戏平台 退出3471815256 iO ...

  6. iOS-Core-Animation-Advanced-Techniques(七)

    转载自:http://www.cocoachina.com/ios/20150106/10840.html Cocos开发者平台 Cocos引擎中文官网 H5轻游戏平台 退出3471815256 iO ...

  7. JAVA 之 UNCAUGHTEXCEPTIONHANDLER异常处理机制

    JAVA 之 UNCAUGHTEXCEPTIONHANDLER异常处理机制 1.JAVA 1.5版本出现的 UNCAUGHTEXCEPTIONHANDLER 当线程由于未捕获异常突然终止时调用的处理程 ...

  8. 【IOS 开发】Objective - C 面向对象高级特性 - 包装类 | 类处理 | 类别 | 扩展 | 协议 | 委托 | 异常处理 | 反射

    一. Objective-C 对象简单处理 1. 包装类 (1) 包装类简介 NSValue 和 NSNumber : -- 通用包装类 NSValue : NSValue 包装单个 short, i ...

  9. iOS获取iPhone系统等信息和服务器返回空的异常处理

    前言: 在项目中经常会遇到需要获取系统的信息来处理一些特殊的需求和服务端返回为空的处理,写在这里只是笔记一下. 获取设备的信息 NSLog(@"globallyUniqueString=%@ ...

最新文章

  1. 开源大数据周刊-第11期
  2. 男女洗澡前后区别,太形象了!
  3. ssh scp 加端口
  4. 算法提高 身份证排序
  5. html转义字符对照表
  6. 径向基函数插值(3)二维数据的插值
  7. 文本文件与二进制文件及编码关系
  8. python中文件路径格式_Python应用-临床路径格式转换
  9. 解决Github加载ipynb文件缓慢/失败
  10. SQL SERVER2000中Transact-SQL语言详解
  11. 机器学习总结(一):线性回归、岭回归、Lasso回归
  12. 19-random猜数
  13. 计算机中丢失了gdiplus.dll,解决 “计算机中丢失gdiplus.dll”
  14. “开闭原则”实现图书售卖简单实现
  15. Android获取百度网盘路径,百度网盘秒传链接生成和提取方法教程
  16. 计算机程序员 面试题库,计算机软考程序员面试题精选题2
  17. Material Dialogs的使用
  18. 基于华三交换机,限制其他网段的IP访问
  19. python爬虫学习爬取股票数据
  20. [4G5G专题-79]:流程 - 4G LTE 寻呼流程Paging

热门文章

  1. PAYPAL 支付,sandbox测试的时候遇到异常:请求被中止: 未能创建 SSL/TLS 安全通道,以及解决方法。
  2. 如何在浏览器中增加Jupyter / ipython笔记本的单元格宽度?
  3. 如何在AngularJS中有条件地应用CSS样式?
  4. 实体框架提供程序类型无法加载?
  5. 在C#中解析命令行参数的最佳方法? [关闭]
  6. win11可以支持win10驱动吗 Windows11更新驱动的步骤方法
  7. SharedPreferences的使用,android
  8. python随机生成一个地区地址_利用Python生成随机的IP地址
  9. TTU智能配电终端_智能终端+正泰云平台,让配电网更“聪明”
  10. Android 8.0系统源码分析--开篇