• (转)IOS崩溃 异常处理(NSSetUncaughtExceptionHandler)


    1. iOS已发布应用中对异常信息捕获和处理  
    2. 代码下载地址:http://download.csdn.net/detail/daiyelang/6740205  
    3.   
    4. iOS开发中我们会遇到程序抛出异常退出的情况,如果是在调试的过程中,异常的信息是一目了然,但是如果是在已经发布的程序中,获取异常的信息有时候是比较困难的。  
    5.    
    6. iOS提供了异常发生的处理API,我们在程序启动的时候可以添加这样的Handler,这样的程序发生异常的时候就可以对这一部分的信息进行必要的处理,适时的反馈给开发者。  
    7.    
    8. 不足的地方是,并不是所有的程序崩溃都是由于发生可以捕捉的异常的,有些时候是因为内存等一些其他的错误导致程序的崩溃,这样的信息是不在这里体现的。  
    9.    
    10. 我做了一个简单的类,进行很基本的操作,可以添加和获取Handler,捕获到异常后将信息写入到app的Documens下的Exception.txt中。  
    11.    
    12. 其实还有很多的处理的办法。  
    13. l  比如可以在程序下一次起来的时候读取这个异常文件发生到服务端。  
    14. l  或者直接就是在处理代码中用openurl的方式(mailto:)调用发送邮件的方式,将异常信息直接变成邮件发送到指定地址。  
    15.    
    16. 以下是完整的代码实现。  
    17.    
    18. 使用场景示例:  
    19.    
    20. #pragma mark -  
    21. #pragma mark Application lifecycle  
    22.    
    23. - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {     
    24.      
    25.     // Override point for customization after application launch.  
    26.       
    27.     [window makeKeyAndVisible];  
    28.      [NdUncaughtExceptionHandler setDefaultHandler];  
    29.      NSArray *array = [NSArray arrayWithObject:@"there is only one objective in this arary,call index one, app will crash and throw an exception!"];  
    30.      NSLog(@"%@", [array objectAtIndex:1]);  
    31.       
    32.      return YES;  
    33. }  
    34.    
    35. 基本接口展示:  
    36.    
    37. #import <Foundation/Foundation.h>  
    38.    
    39. @interface NdUncaughtExceptionHandler : NSObject {  
    40.    
    41. }  
    42.    
    43. + (void)setDefaultHandler;  
    44. + (NSUncaughtExceptionHandler*)getHandler;  
    45.    
    46. @end  
    47. //还可以选择设置自定义的handler,让用户取选择  
    48.    
    49. 接口实现展示  
    50. #import "NdUncaughtExceptionHandler.h"  
    51.    
    52. NSString *applicationDocumentsDirectory() {  
    53.     return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];  
    54. }  
    55.    
    56. void UncaughtExceptionHandler(NSException *exception) {  
    57.      NSArray *arr = [exception callStackSymbols];  
    58.      NSString *reason = [exception reason];  
    59.      NSString *name = [exception name];  
    60.    
    61.      NSString *url = [NSString stringWithFormat:@"=============异常崩溃报告============= name: %@ reason: %@ callStackSymbols: %@",  
    62.                    name,reason,[arr componentsJoinedByString:@" "]];  
    63.      NSString *path = [applicationDocumentsDirectory() stringByAppendingPathComponent:@"Exception.txt"];  
    64.      [url writeToFile:path atomically:YES encoding:NSUTF8StringEncoding error:nil];  
    65.      //除了可以选择写到应用下的某个文件,通过后续处理将信息发送到服务器等  
    66.      //还可以选择调用发送邮件的的程序,发送信息到指定的邮件地址  
    67.      //或者调用某个处理程序来处理这个信息  
    68. }  
    69.    
    70. @implementation NdUncaughtExceptionHandler  
    71.    
    72. -(NSString *)applicationDocumentsDirectory {  
    73.     return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];  
    74. }  
    75.    
    76. + (void)setDefaultHandler  
    77. {  
    78.      NSSetUncaughtExceptionHandler (&UncaughtExceptionHandler);  
    79. }  
    80.    
    81. + (NSUncaughtExceptionHandler*)getHandler  
    82. {  
    83.      return NSGetUncaughtExceptionHandler();  
    84. }  
    85.    
    86. @end  
    87.    
    88. 异常崩溃报告:  
    89. =============异常崩溃报告=============  
    90. name:  
    91. NSRangeException  
    92. reason:  
    93. *** -[NSArray objectAtIndex:]: index 1 beyond bounds [0 .. 0]  
    94. callStackSymbols:  
    95. 0   CoreFoundation                      0x02393919 __exceptionPreprocess + 185  
    96. 1   libobjc.A.dylib                     0x024e15de objc_exception_throw + 47  
    97. 2   CoreFoundation                      0x0238958c -[__NSArrayI objectAtIndex:] + 236  
    98. 3   UncaughtE                           0x000022e8 -[UncaughtEAppDelegate application:didFinishLaunchingWithOptions:] + 157  
    99. 4   UIKit                               0x002b8543 -[UIApplication _callInitializationDelegatesForURL:payload:suspended:] + 1163  
    100. 5   UIKit                               0x002ba9a1 -[UIApplication _runWithURL:payload:launchOrientation:statusBarStyle:statusBarHidden:] + 346  
    101. 6   UIKit                               0x002c4452 -[UIApplication handleEvent:withNewEvent:] + 1958  
    102. 7   UIKit                               0x002bd074 -[UIApplication sendEvent:] + 71  
    103. 8   UIKit                               0x002c1ac4 _UIApplicationHandleEvent + 7495  
    104. 9   GraphicsServices                    0x02bf9afa PurpleEventCallback + 1578  
    105. 10  CoreFoundation                      0x02374dc4 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 52  
    106. 11  CoreFoundation                      0x022d5737 __CFRunLoopDoSource1 + 215  
    107. 12  CoreFoundation                      0x022d29c3 __CFRunLoopRun + 979  
    108. 13  CoreFoundation                      0x022d2280 CFRunLoopRunSpecific + 208  
    109. 14  CoreFoundation                      0x022d21a1 CFRunLoopRunInMode + 97  
    110. 15  UIKit                               0x002ba226 -[UIApplication _run] + 625  
    111. 16  UIKit                               0x002c5b58 UIApplicationMain + 1160  
    112. 17  UncaughtE                           0x00002228 main + 102  
    113. 18  UncaughtE                           0x000021b9 start + 53  
    114.   
    115. 不足的地方是,并不是所有的程序崩溃都是由于发生可以捕捉的异常的,有些时候引起崩溃的大多数原因如:内存访问错误,重复释放等错误就无能为力了,因为这种错误它抛出的是Signal,所以必须要专门做Signal处理。首先定义一个UncaughtExceptionHandler类,.h头文件的代码如下:  
    116.   
    117.   
    118. UncaughtExceptionHandler类,.h头文件的代码如下:  
    119.   
    120. 1  
    121. 2  
    122. 3  
    123. 4  
    124. 5  
    125. 6  
    126. #import  
    127.    
    128. @interface UncaughtExceptionHandler : NSObject{  
    129. BOOL dismissed;  
    130. }  
    131. @end  
    132. 1  
    133. void InstallUncaughtExceptionHandler();  
    134. 然后在.mm文件实现InstallUncaughtExceptionHandler(),如下:  
    135. void InstallUncaughtExceptionHandler(){  
    136. signal(SIGABRT, MySignalHandler);  
    137. signal(SIGILL, MySignalHandler);  
    138. signal(SIGSEGV, MySignalHandler);  
    139. signal(SIGFPE, MySignalHandler);  
    140. signal(SIGBUS, MySignalHandler);  
    141. signal(SIGPIPE, MySignalHandler);  
    142. }  
    143. 这样,当应用发生错误而产生上述Signal后,就将会进入我们自定义的回调函数MySignalHandler。为了得到崩溃时的现场信息,还可以加入一些获取CallTrace及设备信息的代码,.mm文件的完整代码如下:  
    144.   
    145. #import "UncaughtExceptionHandler.h"  
    146. #include #include  
    147.    
    148. NSString * const UncaughtExceptionHandlerSignalExceptionName = @"UncaughtExceptionHandlerSignalExceptionName";  
    149.    
    150. NSString * const UncaughtExceptionHandlerSignalKey = @"UncaughtExceptionHandlerSignalKey";  
    151.    
    152. NSString * const UncaughtExceptionHandlerAddressesKey = @"UncaughtExceptionHandlerAddressesKey";  
    153.    
    154. volatile int32_t UncaughtExceptionCount = 0;  
    155.    
    156. const int32_t UncaughtExceptionMaximum = 10;  
    157.    
    158. const NSInteger UncaughtExceptionHandlerSkipAddressCount = 4;  
    159.    
    160. const NSInteger UncaughtExceptionHandlerReportAddressCount = 5;  
    161.    
    162. @implementation UncaughtExceptionHandler  
    163.    
    164. + (NSArray *)backtrace  
    165.    
    166. {  
    167.    
    168.         void* callstack[128];  
    169.    
    170.      int frames = backtrace(callstack, 128);  
    171.    
    172.      charchar **strs = backtrace_symbols(callstack, frames);       
    173.    
    174.      int i;  
    175.    
    176.      NSMutableArray *backtrace = [NSMutableArray arrayWithCapacity:frames];  
    177.    
    178.      for (  
    179.    
    180.         i = UncaughtExceptionHandlerSkipAddressCount;  
    181.    
    182.         i < UncaughtExceptionHandlerSkipAddressCount +  
    183.    
    184.             UncaughtExceptionHandlerReportAddressCount;  
    185.    
    186.         i++)  
    187.    
    188.      {  
    189.    
    190.         [backtrace addObject:[NSString stringWithUTF8String:strs[i]]];  
    191.    
    192.      }  
    193.    
    194.      free(strs);      
    195.    
    196.      return backtrace;  
    197.    
    198. }  
    199.    
    200. - (void)alertView:(UIAlertView *)anAlertView clickedButtonAtIndex:(NSInteger)anIndex  
    201.    
    202. {  
    203.    
    204.     if (anIndex == 0)  
    205.    
    206.     {  
    207.    
    208.         dismissed = YES;  
    209.    
    210.     }  
    211.    
    212. }  
    213.    
    214. - (void)handleException:(NSException *)exception  
    215.    
    216. {  
    217.    
    218.     UIAlertView *alert =  
    219.    
    220.         [[[UIAlertView alloc]  
    221.    
    222.             initWithTitle:NSLocalizedString(@"Unhandled exception", nil)  
    223.    
    224.             message:[NSString stringWithFormat:NSLocalizedString(  
    225.    
    226.                 @"You can try to continue but the application may be unstable. "  
    227.    
    228.                 @"%@ %@", nil),  
    229.    
    230.                 [exception reason],  
    231.    
    232.                 [[exception userInfo] objectForKey:UncaughtExceptionHandlerAddressesKey]]  
    233.    
    234.             delegate:self  
    235.    
    236.             cancelButtonTitle:NSLocalizedString(@"Quit", nil)  
    237.    
    238.             otherButtonTitles:NSLocalizedString(@"Continue", nil), nil nil]  
    239.    
    240.         autorelease];  
    241.    
    242.     [alert show];     
    243.    
    244.     CFRunLoopRef runLoop = CFRunLoopGetCurrent();  
    245.    
    246.     CFArrayRef allModes = CFRunLoopCopyAllModes(runLoop);     
    247.    
    248.     while (!dismissed)  
    249.    
    250.     {  
    251.    
    252.         for (NSString *mode in (NSArray *)allModes)  
    253.    
    254.         {  
    255.    
    256.             CFRunLoopRunInMode((CFStringRef)mode, 0.001, false);  
    257.    
    258.         }  
    259.    
    260.     }    
    261.    
    262.     CFRelease(allModes);  
    263.    
    264.     NSSetUncaughtExceptionHandler(NULL);  
    265.    
    266.     signal(SIGABRT, SIG_DFL);  
    267.    
    268.     signal(SIGILL, SIG_DFL);  
    269.    
    270.     signal(SIGSEGV, SIG_DFL);  
    271.    
    272.     signal(SIGFPE, SIG_DFL);  
    273.    
    274.     signal(SIGBUS, SIG_DFL);  
    275.    
    276.     signal(SIGPIPE, SIG_DFL);     
    277.    
    278.     if ([[exception name] isEqual:UncaughtExceptionHandlerSignalExceptionName])  
    279.    
    280.     {  
    281.  <span style="white-space:pre"> </span>kill(getpid(), [[[exception userInfo] objectForKey:UncaughtExceptionHandlerSignalKey] intValue]);  
    282.     }  
    283.     else  
    284.     {  
    285.         [exception raise];  
    286.     }  
    287. }  
    288. @end  
    289.    
    290. NSString* getAppInfo() { NSString *appInfo = [NSString stringWithFormat:@"App : %@ %@(%@) Device : %@ OS Version : %@ %@ UDID : %@ ", [[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)  
    291. {  
    292. return;  
    293. }  
    294.    
    295. NSMutableDictionary *userInfo =  
    296.    
    297. [NSMutableDictionary  
    298.    
    299. dictionaryWithObject:[NSNumber numberWithInt:signal]  
    300.    
    301. forKey:UncaughtExceptionHandlerSignalKey];  
    302.    
    303. NSArray *callStack = [UncaughtExceptionHandler backtrace];  
    304.    
    305. [userInfo  
    306.    
    307. setObject:callStack  
    308.    
    309. forKey:UncaughtExceptionHandlerAddressesKey];  
    310.    
    311. [[[[UncaughtExceptionHandler alloc] init] autorelease]  
    312.    
    313. performSelectorOnMainThread:@selector(handleException:)  
    314.    
    315. withObject:  
    316.    
    317. [NSException  
    318.    
    319. exceptionWithName:UncaughtExceptionHandlerSignalExceptionName  
    320.    
    321. reason:  
    322.    
    323. [NSString stringWithFormat:  
    324.    
    325. NSLocalizedString(@"Signal %d was raised. "  
    326.    
    327. @"%@", nil),  
    328.    
    329. signal, getAppInfo()]  
    330.    
    331. userInfo:  
    332.    
    333. [NSDictionary  
    334.    
    335. dictionaryWithObject:[NSNumber numberWithInt:signal]  
    336.    
    337. forKey:UncaughtExceptionHandlerSignalKey]]  
    338.    
    339. waitUntilDone:YES];  
    340.    
    341. }  
    342.    
    343. void InstallUncaughtExceptionHandler()  
    344.    
    345. {  
    346.    
    347. signal(SIGABRT, MySignalHandler);  
    348.    
    349. signal(SIGILL, MySignalHandler);  
    350.    
    351. signal(SIGSEGV, MySignalHandler);  
    352.    
    353. signal(SIGFPE, MySignalHandler);  
    354.    
    355. signal(SIGBUS, MySignalHandler);  
    356.    
    357. signal(SIGPIPE, MySignalHandler);  
    358.    
    359. }  
    360. 在应用自身的 didFinishLaunchingWithOptions 前,加入一个函数:  
    361.   
    362. - (void)installUncaughtExceptionHandler  
    363. {  
    364. InstallUncaughtExceptionHandler();  
    365. }  
    366. 最后,在 didFinishLaunchingWithOptions 中加入这一句代码就行了:  
    367.   
    368. 1  
    369. [self InstallUncaughtExceptionHandler];  
    370. 现在,基本上所有崩溃都能Hold住了。崩溃时将会显示出如下的对话框:  
    371.   
    372. iOS开发23:通过归档永久存储数据 - 双子座的个人页面 - 开源中国社区  
    373.   
    374. 这样在崩溃时还能从容地弹出对话框,比起闪退来,用户也不会觉得那么不爽。然后在下次启动时还可以通过邮件来发送Crash文件到邮箱,这就看各个应用的需求了。  
  • 相关阅读:
    mui的相关知识
    4. 本地的json格式调用方法
    DOM树节点的相关知识
    3.函数引用中“值传参”与“引用传参”的区别
    一,数组的创建 数组的遍历
    重载<<
    SendMessage、PostMessage、PeekMessage、GetMessage、PreTreslateMessage等
    TranslateMessage
    怎样在整个类中恒定常量
    格式化输出
  • 原文地址:https://www.cnblogs.com/seth-chen/p/4362076.html
Copyright © 2020-2023  润新知