iOS強(qiáng)大的crash, 攔截崩潰, 防止閃退

做程序員的都知道,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è)小提醒不讓用戶迷茫就行.閑話少說, 看代碼.

  1. 首先在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ī), 不要連接電腦就可以了.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 首先介紹一款工具 定位解析信號異常上github上搜索DSYMTools,和郵件發(fā)送SKPSMTPMessage ...
    coderK閱讀 787評論 0 1
  • *面試心聲:其實(shí)這些題本人都沒怎么背,但是在上海 兩周半 面了大約10家 收到差不多3個(gè)offer,總結(jié)起來就是把...
    Dove_iOS閱讀 27,628評論 30 472
  • 我們常常會(huì)聽說 Objective-C 是一門動(dòng)態(tài)語言,那么這個(gè)「動(dòng)態(tài)」表現(xiàn)在哪呢?我想最主要的表現(xiàn)就是 Obje...
    Ethan_Struggle閱讀 2,346評論 0 7
  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 2,077評論 0 9
  • 我到我婆家的路要從205國道走,205國道旁有倆個(gè)公墓區(qū),蕪湖市城里的人在冬至?xí)ド蠅灒袝r(shí)我開車從那經(jīng)過時(shí)會(huì)想以...
    胡小花_閱讀 213評論 0 0

友情鏈接更多精彩內(nèi)容