iOS中App崩潰問題是在所難免的,那如何在崩潰之前給用戶一些提示至会,或是可以將這些崩潰信息發(fā)送給自己离咐,以便于自己修正呢?前段時間做項目時奋献,一直沒添加這方面的功能健霹,這兩天沒事從網(wǎng)上看了些資料,順便自己做了個Demo測試了一下瓶蚂,今天把我了解的東西整理一下糖埋。
IOS SDK中提供了一個現(xiàn)成的函數(shù)NSSetUncaughtExceptionHandler 用來做異常處理,但功能非常有限窃这,而引起崩潰的大多數(shù)原因如:內(nèi)存訪問錯誤瞳别,重復(fù)釋放等錯誤就無能為力了,因為這種錯誤它拋出的是Signal杭攻,所以必須要專門做Signal處理祟敛。
Demo運行環(huán)境:Xcode7.3, iOS9.3
步驟一:定義一個UncaughtExceptionHandler類
UncaughtExceptionHandler.h內(nèi)容:
#import <UIKit/UIKit.h>
@interface UncaughtExceptionHandler : NSObject
{
BOOL dismissed;
}
@end
void InstallUncaughtExceptionHandler();
UncaughtExceptionHandler.m內(nèi)容:
#import "UncaughtExceptionHandler.h"
#include <libkern/OSAtomic.h>
#include <execinfo.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
+ (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 = 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(@"程序出現(xiàn)問題啦", nil) message:[NSString stringWithFormat:NSLocalizedString(@"崩潰信息\n"@"%@\n%@", nil), [exception reason], [[exception userInfo] objectForKey:UncaughtExceptionHandlerAddressesKey]] delegate:self cancelButtonTitle:NSLocalizedString(@"離開", nil) otherButtonTitles:nil];
[alert show];
CFRunLoopRef runLoop = CFRunLoopGetCurrent();
CFArrayRef allModes = CFRunLoopCopyAllModes(runLoop);
while (!dismissed)
{
for (NSString *mode in (__bridge 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])
{
kill(getpid(), [[[exception userInfo] objectForKey:UncaughtExceptionHandlerSignalKey] intValue]);
}
else
{
[exception raise];
}
}
@end
NSString* getAppInfo()
{
NSString *appInfo = [NSString stringWithFormat:@"App : %@ %@(%@)\nDevice : %@\nOS Version : %@ %@\n",
[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"],
[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"],
[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"],
[UIDevice currentDevice].model,
[UIDevice currentDevice].systemName,
[UIDevice currentDevice].systemVersion];
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];
// 創(chuàng)建一個OC異常對象
NSException *ex = [NSException exceptionWithName:UncaughtExceptionHandlerSignalExceptionName reason:[NSString stringWithFormat:NSLocalizedString(@"Signal %d was raised.\n"@"%@", nil), signal, getAppInfo()] userInfo:userInfo];
// 處理異常消息
[[[UncaughtExceptionHandler alloc] init] performSelectorOnMainThread:@selector(handleException:) withObject:ex waitUntilDone:YES];
}
void InstallUncaughtExceptionHandler()
{
signal(SIGABRT, MySignalHandler);
signal(SIGILL, MySignalHandler);
signal(SIGSEGV, MySignalHandler);
signal(SIGFPE, MySignalHandler);
signal(SIGBUS, MySignalHandler);
signal(SIGPIPE, MySignalHandler);
}
步驟二:在AppDelegate中導(dǎo)入本類
#import "UncaughtExceptionHandler.h"
- (void)installUncaughtExceptionHandler
{
InstallUncaughtExceptionHandler();
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[self installUncaughtExceptionHandler];
return YES;
}
效果圖如下:
崩潰截圖.png
當(dāng)然,也可以把這些崩潰信息通過郵箱發(fā)送給自己兆解,這個根據(jù)項目需求來自定義馆铁。