IOS崩潰 異常處理(NSSetUncaughtExceptionHandler)


  • iOS已發布應用中對異常信息捕獲和處理  
  • 代碼下載地址:http://download.csdn.net/detail/daiyelang/6740205  
  •   
  • iOS開發中我們會遇到程序拋出異常退出的情況,如果是在調試的過程中,異常的信息是一目了然,但是如果是在已經發布的程序中,獲取異常的信息有時候是比較困難的。  
  •    
  • iOS提供了異常發生的處理API,我們在程序啟動的時候可以添加這樣的Handler,這樣的程序發生異常的時候就可以對這一部分的信息進行必要的處理,適時的反饋給開發者。  
  •    
  • 不足的地方是,並不是所有的程序崩潰都是由於發生可以捕捉的異常的,有些時候是因為內存等一些其他的錯誤導致程序的崩潰,這樣的信息是不在這里體現的。  
  •    
  • 我做了一個簡單的類,進行很基本的操作,可以添加和獲取Handler,捕獲到異常后將信息寫入到app的Documens下的Exception.txt中。  
  •    
  • 其實還有很多的處理的辦法。  
  • l  比如可以在程序下一次起來的時候讀取這個異常文件發生到服務端。  
  • l  或者直接就是在處理代碼中用openurl的方式(mailto:)調用發送郵件的方式,將異常信息直接變成郵件發送到指定地址。  
  •    
  • 以下是完整的代碼實現。  
  •    
  • 使用場景示例:  
  •    
  • #pragma mark -  
  • #pragma mark Application lifecycle  
  •    
  • - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {     
  •      
  •     // Override point for customization after application launch.  
  •       
  •     [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);  
  •    
  •      charchar **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 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住了。崩潰時將會顯示出如下的對話框:  
  •   
  • iOS開發23:通過歸檔永久存儲數據 - 雙子座的個人頁面 - 開源中國社區  
  •   
  • 這樣在崩潰時還能從容地彈出對話框,比起閃退來,用戶也不會覺得那么不爽。然后在下次啟動時還可以通過郵件來發送Crash文件到郵箱,這就看各個應用的需求了。

  • 免責聲明!

    本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



     
    粵ICP備18138465號   © 2018-2025 CODEPRJ.COM