开发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]); |