开发iOS应用,解决Crash问题始终是一个难题。Crash分为两种,一种是由EXC_BAD_ACCESS引起的,原因是访问了不属于本进程的内存地址,有可能是访问已被释放的内存;另一种是未被捕获的Objective-C异常(NSException),导致程序向自身发送了SIGABRT信号而崩溃。其实对于未捕获的Objective-C异常,我们是有办法将它记录下来的,如果日志记录得当,能够解决绝大部分崩溃的问题。这里对于UI线程与后台线程分别说明。
一:在.h文件中编写
@interface UncaughtExceptionHandler : NSObject{ |
void HandleException(NSException *exception); |
void SignalHandler(int signal); |
void InstallUncaughtExceptionHandler(void); |
二:在.m文件编写
#import "UncaughtExceptionHandler.h" |
#include <libkern/OSAtomic.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 |
int frames = backtrace(callstack, 128); |
char **strs = backtrace_symbols(callstack, frames); |
NSMutableArray *backtrace = [NSMutableArray arrayWithCapacity:frames]; |
i = UncaughtExceptionHandlerSkipAddressCount; |
i < UncaughtExceptionHandlerSkipAddressCount + |
UncaughtExceptionHandlerReportAddressCount; |
[backtrace addObject:[NSString stringWithUTF8String:strs[i]]]; |
- (void)alertView:(UIAlertView *)anAlertView clickedButtonAtIndex:(NSInteger)anIndex |
- (void)validateAndSaveCriticalApplicationData |
- (void)handleException:(NSException *)exception |
[self validateAndSaveCriticalApplicationData]; |
initWithTitle:NSLocalizedString(@"抱歉,程序出现了异常", nil) |
message:[NSString stringWithFormat:NSLocalizedString( |
@"如果点击继续,程序有可能会出现其他的问题,建议您还是点击退出按钮并重新打开\n\n" |
@"异常原因如下:\n%@\n%@", nil), |
[[exception userInfo] objectForKey:UncaughtExceptionHandlerAddressesKey]] |
cancelButtonTitle:NSLocalizedString(@"退出", nil) |
otherButtonTitles:NSLocalizedString(@"继续", nil), nil] |
CFRunLoopRef runLoop = CFRunLoopGetCurrent(); |
CFArrayRef allModes = CFRunLoopCopyAllModes(runLoop); |
for (NSString *mode in (NSArray *)allModes) |
CFRunLoopRunInMode((CFStringRef)mode, 0.001, false); |
NSSetUncaughtExceptionHandler(NULL); |
signal(SIGABRT, SIG_DFL); |
signal(SIGSEGV, SIG_DFL); |
signal(SIGPIPE, SIG_DFL); |
if ([[exception name] isEqual:UncaughtExceptionHandlerSignalExceptionName]) |
kill(getpid(), [[[exception userInfo] objectForKey:UncaughtExceptionHandlerSignalKey] intValue]); |
void HandleException(NSException *exception) |
int32_t exceptionCount = OSAtomicIncrement32(&UncaughtExceptionCount); |
if (exceptionCount > UncaughtExceptionMaximum) |
NSArray *callStack = [UncaughtExceptionHandler backtrace]; |
NSMutableDictionary *userInfo = |
[NSMutableDictionary dictionaryWithDictionary:[exception userInfo]]; |
forKey:UncaughtExceptionHandlerAddressesKey]; |
[[[[UncaughtExceptionHandler alloc] init] autorelease] |
performSelectorOnMainThread:@selector(handleException:) |
exceptionWithName:[exception name] |
reason:[exception reason] |
void SignalHandler(int signal) |
int32_t exceptionCount = OSAtomicIncrement32(&UncaughtExceptionCount); |
if (exceptionCount > UncaughtExceptionMaximum) |
NSMutableDictionary *userInfo = |
dictionaryWithObject:[NSNumber numberWithInt:signal] |
forKey:UncaughtExceptionHandlerSignalKey]; |
NSArray *callStack = [UncaughtExceptionHandler backtrace]; |
forKey:UncaughtExceptionHandlerAddressesKey]; |
[[[[UncaughtExceptionHandler alloc] init] autorelease] |
performSelectorOnMainThread:@selector(handleException:) |
exceptionWithName:UncaughtExceptionHandlerSignalExceptionName |
[NSString stringWithFormat: |
NSLocalizedString(@"Signal %d was raised.", nil), |
dictionaryWithObject:[NSNumber numberWithInt:signal] |
forKey:UncaughtExceptionHandlerSignalKey]] |
void InstallUncaughtExceptionHandler(void) |
NSSetUncaughtExceptionHandler(&HandleException); |
signal(SIGABRT, SignalHandler); |
signal(SIGILL, SignalHandler); |
signal(SIGSEGV, SignalHandler); |
signal(SIGFPE, SignalHandler); |
signal(SIGBUS, SignalHandler); |
signal(SIGPIPE, SignalHandler); |
三:最后在AppDelegate中创建
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{ |
InstallUncaughtExceptionHandler(); |
self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease]; |
self.viewController = [[[ViewController alloc] initWithNibName:@"ViewController" bundle:nil] autorelease]; |
self.window.rootViewController = self.viewController; |
[self.window makeKeyAndVisible]; |
四:最后的测试
- (IBAction)onclcko:(id)sender { |
NSArray *arry=[NSArray arrayWithObject:@"sss"]; |
NSLog(@"%@",[arry objectAtIndex:1]); |