iOS捕获全局异常,统一收集

参考博文:http://www.cnblogs.com/easonoutlook/archive/2012/12/27/2835979.html

创新互联建站主营昆都仑网站建设的网络公司,主营网站建设方案,手机APP定制开发,昆都仑h5微信平台小程序开发搭建,昆都仑网站营销推广欢迎昆都仑等地区企业咨询

开发程序的过程中不管我们已经如何小心,总是会在不经意间遇到程序闪退。流畅的操作被无情地Crash打断,当程序运行Crash的时候,系统会把运行的最后时刻的运行信息记录下来,存储到一个文件中,也就是我们所说的Crash文件,当时如果是真机测试离开Xcode的时候Crash掉,我们是无法知道crash的具体位置的。现在做一个程序统一记录crash的位置。先科普一下crash的知识:


Crash文件结构

1、Process Information(进程信息)

Incident Idnetifier崩溃报告的唯一标识符,不同的Crash
CrashReporter Key设备标识相对应的唯一键值(并非真正的设备的UDID,苹果为了保护用户隐私iOS6以后已经无法获取)。通常同一个设备上同一版本的App发生Crash时,该值都是一样的。
Hardware Model代表发生Crash的设备类型,上图中的“iPad4,4”代表iPad Air
Process代表Crash的进程名称,通常都是我们的App的名字, []里面是当时进程的ID
Path可执行程序在手机上的存储位置,注意路径时到XXX.app/XXX,XXX.app其实是作为一个Bundle的,真正的可执行文件其实是Bundle里面的XXX,感兴趣的可以自己查一下相关资料,有机会我后面也会介绍到
Identifier你的App的Indentifier,通常为“com.xxx.yyy”,xxx代表你们公司的域名,yyy代表某一个App
Version当前App的版本号,由Info.plist中的两个字段组成,CFBundleShortVersionString and CFBundleVersion
Code Type当前App的CPU架构
Parent Process当前进程的父进程,由于iOS中App通常都是单进程的,一般父进程都是launchd

2、Basic Information

Date/TimeCrash发生的时间,可读的字符串
OS Version系统版本,()内的数字代表的时Bulid号
Report VersionCrash日志的格式,目前基本上都是104,不同的version里面包含的字段可能有不同

 

 

3、Exception(非常重要)

Exception Type异常类型
Exception Subtype:异常子类型
Crashed Thread发生异常的线程号

 

 

 

4、Thread Backtrace

发生Crash的线程的Crash调用栈,从上到下分别代表调用顺序,最上面的一个表示抛出异常的位置,依次往下可以看到API的调用顺序。上图的信息表明本次Crash出现xxxViewController的323行,出错的函数调用为orderCountLoadFailed。

5、Thread State

Crash时发生时刻,线程的状态,通常我们根据Crash栈即可获取到相关信息,这部分一般不用关心。

6、Binary Images

Crash时刻App加载的所有的库,其中第一行是Crash发生时我们App可执行文件的信息,可以看出为armv7,可执行文件的包得uuid位c0f……cd65,解析Crash的时候dsym文件的uuid必须和这个一样才能完成Crash的符号化解析。

废话说了那么多,到底怎么捕获整个系统的crash呢,入正题,其实也很简单:创建一个工具类  ExceptionHandler.h  和 ExceptionHandler.m(oc版本),swift版本的下面再讲.

ExceptionHandler.h

#import

@interface ExceptionHandler : NSObject{

BOOL dismissed;

}

@end

void HandleException(NSException *exception);

void SignalHandler(int signal);

void InstallUncaughtExceptionHandler(void);

ExceptionHandler.m

#import "ExceptionHandler.h"

#include

#include

NSString *const ExceptionHandlerSignalExceptionName =@"UncaughtExceptionHandlerSignalExceptionName";

NSString *const ExceptionHandlerSignalKey =@"UncaughtExceptionHandlerSignalKey";

NSString *const ExceptionHandlerAddressesKey =@"UncaughtExceptionHandlerAddressesKey";

volatile int32_t UncaughtExceptionCount =0;

const int32_t UncaughtExceptionMaximum =10;

const NSInteger ExceptionHandlerSkipAddressCount =4;

const NSInteger ExceptionHandlerReportAddressCount =5;

@implementation ExceptionHandler

+ (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 = ExceptionHandlerSkipAddressCount;

i < ExceptionHandlerSkipAddressCount +

ExceptionHandlerReportAddressCount;

i++)

{

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

}

free(strs);

 

return backtrace;

}

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

{

if (anIndex ==0)

{

dismissed =YES;

}

}

- (void)validateAndSaveCriticalApplicationData

{

}

- (void)handleException:(NSException *)exception

{

[self validateAndSaveCriticalApplicationData];

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"

@"Debug details follow:\n%@\n%@",nil),

[exception reason],

[[exception userInfo] objectForKey:ExceptionHandlerAddressesKey]]

delegate:self

cancelButtonTitle:NSLocalizedString(@"Quit",nil)

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

[alert show];

    

    NSUserDefaults*defaults = [NSUserDefaults standardUserDefaults];

    

CFRunLoopRef runLoop = CFRunLoopGetCurrent();

CFArrayRef allModes = CFRunLoopCopyAllModes(runLoop);

while (!dismissed)

{

for (NSString *modein (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:ExceptionHandlerSignalExceptionName])

{

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

}

else

{

[exception raise];

}

}

@end

void HandleException(NSException *exception)

{

int32_t exceptionCount = OSAtomicIncrement32(&UncaughtExceptionCount);

if (exceptionCount > UncaughtExceptionMaximum)

{

return;

}

NSArray *callStack = [ExceptionHandler backtrace];

NSMutableDictionary *userInfo =

[NSMutableDictionary dictionaryWithDictionary:[exception userInfo]];

[userInfo

setObject:callStack

forKey:ExceptionHandlerAddressesKey];

[[[ExceptionHandler alloc] init]

performSelectorOnMainThread:@selector(handleException:)

withObject:

[NSException

exceptionWithName:[exception name]

reason:[exception reason]

userInfo:userInfo]

waitUntilDone:YES];

}

void SignalHandler(int signal)

{

int32_t exceptionCount = OSAtomicIncrement32(&UncaughtExceptionCount);

if (exceptionCount > UncaughtExceptionMaximum)

{

return;

}

NSMutableDictionary *userInfo =

[NSMutableDictionary

dictionaryWithObject:[NSNumber numberWithInt:signal]

forKey:ExceptionHandlerSignalKey];

NSArray *callStack = [ExceptionHandler backtrace];

[userInfo

setObject:callStack

forKey:ExceptionHandlerAddressesKey];

[[[ExceptionHandler alloc] init]

performSelectorOnMainThread:@selector(handleException:)

withObject:

[NSException

exceptionWithName:ExceptionHandlerSignalExceptionName

reason:

[NSString stringWithFormat:

NSLocalizedString(@"Signal %d was raised.",nil),

signal]

userInfo:

[NSDictionary dictionaryWithObject:[NSNumber numberWithInt:signal] forKey:ExceptionHandlerSignalKey]]

waitUntilDone:YES];

}

void InstallUncaughtExceptionHandler(void)

{

NSSetUncaughtExceptionHandler(&HandleException);

signal(SIGABRT, SignalHandler);

signal(SIGILL, SignalHandler);

signal(SIGSEGV, SignalHandler);

signal(SIGFPE, SignalHandler);

signal(SIGBUS, SignalHandler);

signal(SIGPIPE, SignalHandler);

}

使用方式:

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

    

    InstallUncaughtExceptionHandler();

   returnYES;

}

大功告成

swift版本:

func application(application:UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject:AnyObject]?) ->Bool {

          

       //输出bug信息

        print("系统bug日志记录---------------------\(NSUserDefaults.standardUserDefaults().valueForKey(ERROR_MESSAGE))")

        NSSetUncaughtExceptionHandler { exceptionin

           var message = exception.callStackSymbols

            message.removeAll()

            message.append("错误理由:\(exception.reason!)")

            message.append("错误详细信息:\(exception.callStackSymbols)")

            NSUserDefaults.standardUserDefaults().setObject(message, forKey: ERROR_MESSAGE)

            NSUserDefaults.standardUserDefaults().synchronize()

            print("系统bug日志记录---------------------\(message)")

        }

       returntrue

    }

    

swift比oc简单很多,直接在didFinishLaunchingWithOptions加入上面的代码就可以。

测试代码:

 NSMutableArray *array = [NSMutableArray array];

    [array addObject:@"1"];

    [array addObject:@"2"];

    

    NSLog(@"%@",array[9]);

效果图  数组越界:

iOS 捕获全局异常,统一收集









本文标题:iOS捕获全局异常,统一收集
标题URL:http://myzitong.com/article/pgjjop.html