做程序員的都知道,BUG永遠(yuǎn)是改不完的,如果已經(jīng)上線的版本出現(xiàn)閃退的情況, 會(huì)讓用戶體驗(yàn)非常不好.所以天朝的騎士們搞出了這個(gè)叫crash的東西,大致的道理呢,就是利用RunLoop,來判斷要不要進(jìn)行下一步操作,在你的代碼出錯(cuò)的時(shí)候, 例如數(shù)組越界,那么就會(huì)觸發(fā)這個(gè)機(jī)制,RunLoop就會(huì)告訴你,可以自定義彈出框來告訴用戶下一步會(huì)出問題不要進(jìn)行下一步了,或者你自己強(qiáng)制不進(jìn)行下一步,加一個(gè)小提醒不讓用戶迷茫就行.閑話少說, 看代碼.
- 首先在AppDelegate.m的- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions ;方法里,添加這句代碼:
InstallUncaughtExceptionHandler();// 攔截崩潰.
當(dāng)然,這個(gè)也可以在你建立好崩潰攔截的類后來添加.都是一樣的.
生成一個(gè)繼承于NSObject的類, .h文件里寫上下面這些就夠了
.h 文件代碼:
import <Foundation/Foundation.h>
import <UIKit/UIKit.h>
@interface UncaughtExceptionHandler : NSObject
{
BOOL dismissed;
}
@end
void HandleException(NSException *exception);
void SignalHandler(int signal);
void InstallUncaughtExceptionHandler(void);
然后就是核心代碼了,.會(huì)為你一一呈現(xiàn)
.m 文件代碼:
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
// 程序崩潰2(程序崩潰第二步走的方法)
- (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;
}else if (anIndex==1) {
NSLog(@"ssssssss");
}
} - (void)validateAndSaveCriticalApplicationData {
// 崩潰攔截可以做的事,寫在這個(gè)方法也是極好的
}
// 程序崩潰3(程序崩潰是第三進(jìn)入的方法) - (void)handleException:(NSException *)exception {
[self validateAndSaveCriticalApplicationData];
//這里可以打印或者顯示出ERROR的原因.
// NSString *message = [NSString stringWithFormat:NSLocalizedString(@"如果點(diǎn)擊繼續(xù),程序有可能會(huì)出現(xiàn)其他的問題,建議您還是點(diǎn)擊退出按鈕并重新打開"@"異常原因如下:\n%@\n%@",nil),[exception reason],[[exception userInfo] objectForKey:UncaughtExceptionHandlerAddressesKey]];
//設(shè)置彈出框來了提醒用戶, 當(dāng)然也可以是自己設(shè)計(jì)其他內(nèi)容,
NSString *message = [NSString stringWithFormat:NSLocalizedString(@"如果點(diǎn)擊繼續(xù),程序有可能會(huì)出現(xiàn)其他的問題,建議您還是點(diǎn)擊退出按鈕并重新打開",nil)];
UIAlertView *alert =[[UIAlertView alloc]initWithTitle:NSLocalizedString(@"抱歉,程序出現(xiàn)了異常",nil)
message:message
delegate:self
cancelButtonTitle:NSLocalizedString(@"繼續(xù)",nil)
otherButtonTitles:NSLocalizedString(@"退出",nil), nil];
[alert show];
// 利用RunLoop , 來完成攔截的操作
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);
[exception raise];
if ([[exception name] isEqual:UncaughtExceptionHandlerSignalExceptionName]) {
kill(getpid(), [[[exception userInfo] objectForKey:UncaughtExceptionHandlerSignalKey]intValue]);
}else{
[exception raise];
}
}
@end
// 程序崩潰時(shí)1(程序崩潰是首先進(jìn)入的方法, 你可以debug自己調(diào)試)
void HandleException(NSException *exception) {
int32_t exceptionCount =OSAtomicIncrement32(&UncaughtExceptionCount);
if (exceptionCount >UncaughtExceptionMaximum) {
return;
}
NSArray *callStack = [UncaughtExceptionHandler backtrace];
NSMutableDictionary *userInfo =[NSMutableDictionary dictionaryWithDictionary:[exception userInfo]];[userInfo setObject:callStack forKey:UncaughtExceptionHandlerAddressesKey];
[[[UncaughtExceptionHandler alloc] init]performSelectorOnMainThread:@selector(handleException:)withObject:
[NSException exceptionWithName:[exception name] reason:[exception reason] userInfo:userInfo]waitUntilDone:YES];
}
void SignalHandler(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] performSelectorOnMainThread:@selector(handleException:)withObject:[NSException exceptionWithName:UncaughtExceptionHandlerSignalExceptionName reason:[NSString stringWithFormat:NSLocalizedString(@"Signal %d was raised.",nil),signal]userInfo:
[NSDictionary dictionaryWithObject:[NSNumber numberWithInt:signal]forKey:UncaughtExceptionHandlerSignalKey]]waitUntilDone:YES];
}
//. 進(jìn)入程序時(shí)(在AppDelegate.m)里添加那行代碼后,就會(huì)啟用這行代碼了
void InstallUncaughtExceptionHandler(void) {
NSSetUncaughtExceptionHandler(&HandleException);
signal(SIGHUP, SignalHandler);
signal(SIGINT, SignalHandler);
signal(SIGQUIT, SignalHandler);
signal(SIGABRT,SignalHandler);
signal(SIGILL,SignalHandler);
signal(SIGSEGV,SignalHandler);
signal(SIGFPE,SignalHandler);
signal(SIGBUS,SignalHandler);
signal(SIGPIPE,SignalHandler);
}
//好啦,這些就完全夠了,當(dāng)你的程序遇到崩潰的時(shí)候, 就會(huì)穩(wěn)穩(wěn)的跳出提示信息, 繼續(xù)就真的崩潰的, 退出就不會(huì)進(jìn)行下一步操作了.還有一點(diǎn)要注意,當(dāng)你連到真機(jī)上測試的時(shí)候,這個(gè)崩潰只會(huì)提示一次,你再次點(diǎn)擊的時(shí)候他就不彈出提示而是直接崩潰了,所以你要是測試的話,把程序運(yùn)行后, 拔掉真機(jī), 不要連接電腦就可以了.