iOS獲取App崩潰信息

app上線后在某些情況下肯定會有崩潰現(xiàn)象發(fā)生
獲取崩潰信息的方式有很多,比較常見的是使用友盟統(tǒng)計、騰訊Bugly等.但是需要集成第三方框架,并且信息傳到第三方服務(wù)器上,如果公司很反感第三方框架,或者很注重數(shù)據(jù)安全和隱私,或者是資金問題,是不會采用這種方式的.
還有就是我目前這個項目使用的:使用原生框架應(yīng)用內(nèi)崩潰收集,并上傳服務(wù)器

崩潰信息處理主要分兩步:

1.在用戶app拋出異常的時候,我們捕獲到異常并處理(例如搞個彈窗)
2.將異常寫入沙盒中或上傳到服務(wù)器

如何捕獲異常

iOS 原生的類NSException可以用來做異常處理,但功能有限,而引起崩潰的大多數(shù)原因如:內(nèi)存訪問錯誤,重復(fù)釋放等錯誤就無能為力了
因為這種錯誤它拋出的是signal,所以還要做signal處理.

我目前的項目中做了兩種處理.下面代碼注釋很詳細(xì).
(關(guān)于NSException和signal詳細(xì)的請閱讀文末的參考鏈接,前輩們寫得很好我就不重復(fù)寫了)

// 將獲取崩潰信息函數(shù)寫在AppDelegate這個方法中.以保證在程序開始運行就具有獲取崩潰信息的功能 
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 
{ 
    //code...
    //調(diào)用自定義類中的收集崩潰信息的方法
    [CJAppUncaughtExceptionHandler InstallUncaughtExceptionHandler];
    //code...
    return YES; 
}

下面是在自定義類的.m文件中的代碼,.h中就一個方法聲明,下面有注釋

#include <libkern/OSAtomic.h>
#include <execinfo.h>
//未捕獲的異常處理器信號異常的名字
NSString * const UncaughtExceptionHandlerSignalExceptionName    = @"UncaughtExceptionHandlerSignalExceptionName";
//異常處理程序信號保存到字典的key
NSString * const UncaughtExceptionHandlerSignalKey              = @"UncaughtExceptionHandlerSignalKey";
NSString * const UncaughtExceptionHandlerAddressesKey           = @"UncaughtExceptionHandlerAddressesKey";
//當(dāng)前處理異常個數(shù)
volatile int32_t UncaughtExceptionCount                         = 0;
//最大能夠處理的異常個數(shù)
const int32_t    UncaughtExceptionMaximum                       = 10;
//這兩個預(yù)留的,暫時用不到
const NSInteger  UncaughtExceptionHandlerSkipAddressCount       = 4;
const NSInteger  UncaughtExceptionHandlerReportAddressCount     = 5;

#import "CJAppUncaughtExceptionHandler.h"

void InstallUncaughtExceptionHandler();
@implementation CJAppUncaughtExceptionHandler

NSUncaughtExceptionHandler* _uncaughtExceptionHandler = nil;

//暴露在.h中,在AppDelegate中調(diào)用,保證在程序開始運行就具有獲取崩潰信息的功能
+ (void)InstallUncaughtExceptionHandler
{
    InstallUncaughtExceptionHandler();
}

//創(chuàng)建異常對象調(diào)用
+ (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
                         ];
    return appInfo;
}

//ExceptionSignalHandler()會調(diào)用
+ (NSArray *)backtrace
{
    //定義一個指針數(shù)組
    void* callstack[128];
    //該函數(shù)用于獲取當(dāng)前線程的調(diào)用堆棧,獲取的信息將會被存放在callstack中隆圆。
    //參數(shù)128用來指定callstack中可以保存多少個void* 元素浩习。
    //函數(shù)返回值是實際獲取的指針個數(shù),最大不超過128大小在callstack中的指針實際是從堆棧中獲取的返回地址,每一個堆椆罟遥框架有一個返回地址艇炎。
    int frames = backtrace(callstack, 128);
    //backtrace_symbols將從backtrace函數(shù)獲取的信息轉(zhuǎn)化為一個字符串?dāng)?shù)組.
    //參數(shù)callstack應(yīng)該是從backtrace函數(shù)獲取的數(shù)組指針,frames是該數(shù)組中的元素個數(shù)(backtrace的返回值)
    //函數(shù)返回值是一個指向字符串?dāng)?shù)組的指針,它的大小同callstack相同.每個字符串包含了一個相對于callstack中對應(yīng)元素的可打印信息.
    char **strs = backtrace_symbols(callstack, frames);

    NSMutableArray *backtrace = [NSMutableArray arrayWithCapacity:frames];

    for (int i=0;i<frames;i++)
    {
        [backtrace addObject:[NSString stringWithUTF8String:strs[i]]];
    }
    //注意釋放
    free(strs);
    return backtrace;
}

//由InstallUncaughtExceptionHandler()調(diào)用
+ (void)InstallUncaughtExceptionHandlerB
{
    //獲取崩潰統(tǒng)計的函數(shù)指針
    _uncaughtExceptionHandler = NSGetUncaughtExceptionHandler();
    
    //利用 NSSetUncaughtExceptionHandler樊展,當(dāng)程序異常退出的時候购城,可以先進行處理,然后做一些自定義的動作
    //其實控制臺輸出的日志信息就是NSException產(chǎn)生的刨疼,一旦程序拋出異常泉唁,程序就會崩潰,控制臺就會有崩潰日志
    NSSetUncaughtExceptionHandler(&UncaughtExceptionHandler);
}

//異常截獲處理方法
void UncaughtExceptionHandler(NSException *exception)
{
    NSLog(@"CRASH: %@", exception);
    NSLog(@"Stack Trace: %@", [exception callStackSymbols]);
    // 異常的堆棧信息
    NSArray *stackArray = [exception callStackSymbols];
    // 出現(xiàn)異常的原因
    NSString *reason = [exception reason];
    // 異常名稱
    NSString *name = [exception name];
    //在沙盒的Documents目錄下創(chuàng)建文件保存//也可以上傳到服務(wù)器或者發(fā)送郵件之類的
    NSDictionary *dic = [NSDictionary dictionaryWithObjectsAndKeys:name,@"exception_name",
                         reason,@"exception_reason",
                         stackArray,@"stack_info", nil];
    NSArray *paths=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES);
    NSString *path=[paths objectAtIndex:0];
    NSString *fileName = [path stringByAppendingPathComponent:@"crash.plist"];
    [dic writeToFile:fileName atomically:YES];
    
}

@end
//捕獲信號后的回調(diào)函數(shù),由InstallUncaughtExceptionHandler()調(diào)用
void ExceptionSignalHandler(int signalval)
{
 int32_t exceptionCount = OSAtomicIncrement32(&UncaughtExceptionCount);
    //如果太多不用處理
   if (exceptionCount > UncaughtExceptionMaximum)
   {
      return;
   }
 
 NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithObject:[NSNumber numberWithInt:signalval]
                                                                         forKey:UncaughtExceptionHandlerSignalKey];
    
    //此方法獲取的棧信息太少
 NSArray *callStack = [CJAppUncaughtExceptionHandler backtrace];
    
 [userInfo setObject:callStack forKey:UncaughtExceptionHandlerAddressesKey];
    
    //創(chuàng)建一個oc異常對象
    NSString *appInfo = [CJAppUncaughtExceptionHandler getAppInfo];
    
    NSString *exceptionReason = [NSString stringWithFormat:@"Signal %d was raised.\n"@"%@", signalval, appInfo];

    NSException *exception = [NSException exceptionWithName:UncaughtExceptionHandlerSignalExceptionName
                                                     reason:exceptionReason
                                                   userInfo:userInfo];

    //在沙盒的Documents目錄下創(chuàng)建文件保存//也可以上傳到服務(wù)器或者發(fā)送郵件之類的
    NSDictionary *dic = [NSDictionary dictionaryWithObjectsAndKeys:
                         exception,@"exception",
                         userInfo,@"stack_info", nil];
    NSArray *paths=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES);
    NSString *path=[paths objectAtIndex:0];
    NSString *fileName = [path stringByAppendingPathComponent:@"crash.plist"];
    [dic writeToFile:fileName atomically:YES];
    
    
    //取消異常捕獲币狠,在應(yīng)用退出或者銷毀的時候設(shè)置游两,只需調(diào)用此函數(shù),參數(shù)為NULL
    NSSetUncaughtExceptionHandler(NULL);
    //signal函數(shù)中的信號處理函數(shù)handler漩绵,可以是用戶指定的一個信號處理函數(shù)贱案,也可以是內(nèi)核特定的函數(shù)指針SIG_IGN或SIG_DFL。
    //若信號句柄是SIG_IGN或SIG_DFL止吐,則分別表示對捕獲的信號采取忽略操作或者默認(rèn)操作宝踪。
    //其實對于大多數(shù)信號的系統(tǒng)默認(rèn)動作是終止該進程。這與不寫此處理函數(shù)是一樣的碍扔。
    signal(SIGHUP, SIG_DFL);
    signal(SIGINT, SIG_DFL);
    signal(SIGQUIT,SIG_DFL);
    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()可以用來送參數(shù)sig 指定的信號給參數(shù)pid 指定的進程瘩燥。
        //getpid()用來取得目前進程的進程識別碼
        //第二個參數(shù)sig即SIGHUP之類的信號
        kill(getpid(), [[[exception userInfo] objectForKey:UncaughtExceptionHandlerSignalKey] intValue]);
   }
   else
   {
        //拋出異常,不做處理
        [exception raise];
   }
}
//注冊崩潰攔截
void InstallUncaughtExceptionHandler()
{
    [CJAppUncaughtExceptionHandler InstallUncaughtExceptionHandlerB];
    //signal信號函數(shù),第一個參數(shù)表示需要處理的信號值不同,第二個參數(shù)為處理函數(shù)
    //由掛起導(dǎo)致的終止信號
    signal(SIGHUP, ExceptionSignalHandler);
    //由中斷導(dǎo)致的終止信號
    signal(SIGINT, ExceptionSignalHandler);
    //由退出導(dǎo)致的終止信號
    signal(SIGQUIT, ExceptionSignalHandler);
    //注冊程序由于abort()函數(shù)調(diào)用發(fā)生的程序終止信號
    signal(SIGABRT, ExceptionSignalHandler);
    //注冊程序由于非法指令產(chǎn)生的程序終止信號
    signal(SIGILL, ExceptionSignalHandler);
    //注冊程序由于無效內(nèi)存的引用導(dǎo)致的程序終止信號
    signal(SIGSEGV, ExceptionSignalHandler);
    //注冊程序由于浮點數(shù)異常導(dǎo)致的程序終止信號
    signal(SIGFPE, ExceptionSignalHandler);
    //注冊程序由于內(nèi)存地址未對齊導(dǎo)致的程序終止信號
    signal(SIGBUS, ExceptionSignalHandler);
    //程序通過端口發(fā)送消息失敗導(dǎo)致的程序終止信號
    signal(SIGPIPE, ExceptionSignalHandler);
}

同時還要注意不要用try-catch語句捕捉NSException異常,
因為在Cocoa Touch 框架中,引發(fā)自己的異常然后再用try-catch語句塊來捕捉,這非常消耗資源,
但是捕捉系統(tǒng)引發(fā)的異常就不會

參考相關(guān)鏈接:
1.http://blog.csdn.net/yhhwatl/article/details/34432603?utm_source=tuicool&utm_medium=referral
2.http://www.reibang.com/p/77660e626874#
3.http://www.cnblogs.com/daxiaxiaohao/p/4466097.html?utm_source=tuicool&utm_medium=referral
4.http://www.cnblogs.com/mickole/p/3246702.html
5.http://blog.sina.com.cn/s/blog_8184e03301013ddz.html
6.http://baike.baidu.com/link?url=RT6L_RWOcmCOICq3BuEcdTQfRlqgsaNmLUnGn2QrGBtIkldsqhMBmOgSm-CQvcH-h3uqupCf8aS98Xl3Bm1Ctq
7.https://nianxi.net/ios/ios-crash-reporter.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末厉膀,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子二拐,更是在濱河造成了極大的恐慌服鹅,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,539評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件百新,死亡現(xiàn)場離奇詭異企软,居然都是意外死亡,警方通過查閱死者的電腦和手機饭望,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評論 3 396
  • 文/潘曉璐 我一進店門仗哨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來形庭,“玉大人,你說我怎么就攤上這事厌漂∪眩” “怎么了?”我有些...
    開封第一講書人閱讀 165,871評論 0 356
  • 文/不壞的土叔 我叫張陵桩卵,是天一觀的道長验靡。 經(jīng)常有香客問我倍宾,道長雏节,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,963評論 1 295
  • 正文 為了忘掉前任高职,我火速辦了婚禮钩乍,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘怔锌。我一直安慰自己寥粹,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,984評論 6 393
  • 文/花漫 我一把揭開白布埃元。 她就那樣靜靜地躺著涝涤,像睡著了一般。 火紅的嫁衣襯著肌膚如雪岛杀。 梳的紋絲不亂的頭發(fā)上阔拳,一...
    開封第一講書人閱讀 51,763評論 1 307
  • 那天,我揣著相機與錄音类嗤,去河邊找鬼糊肠。 笑死,一個胖子當(dāng)著我的面吹牛遗锣,可吹牛的內(nèi)容都是我干的货裹。 我是一名探鬼主播,決...
    沈念sama閱讀 40,468評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼精偿,長吁一口氣:“原來是場噩夢啊……” “哼弧圆!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起笔咽,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤搔预,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后拓轻,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體斯撮,經(jīng)...
    沈念sama閱讀 45,850評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,002評論 3 338
  • 正文 我和宋清朗相戀三年扶叉,在試婚紗的時候發(fā)現(xiàn)自己被綠了勿锅。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片帕膜。...
    茶點故事閱讀 40,144評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖溢十,靈堂內(nèi)的尸體忽然破棺而出垮刹,到底是詐尸還是另有隱情,我是刑警寧澤张弛,帶...
    沈念sama閱讀 35,823評論 5 346
  • 正文 年R本政府宣布荒典,位于F島的核電站,受9級特大地震影響吞鸭,放射性物質(zhì)發(fā)生泄漏寺董。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,483評論 3 331
  • 文/蒙蒙 一刻剥、第九天 我趴在偏房一處隱蔽的房頂上張望遮咖。 院中可真熱鬧,春花似錦造虏、人聲如沸御吞。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽陶珠。三九已至,卻和暖如春享钞,著一層夾襖步出監(jiān)牢的瞬間揍诽,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評論 1 272
  • 我被黑心中介騙來泰國打工嫩与, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留寝姿,地道東北人。 一個月前我還...
    沈念sama閱讀 48,415評論 3 373
  • 正文 我出身青樓划滋,卻偏偏與公主長得像饵筑,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子处坪,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,092評論 2 355

推薦閱讀更多精彩內(nèi)容