iOS - 崩潰異常處理(1)

https://www.jianshu.com/p/4d32664dcfdb

 

1、關於崩潰

閃退估計是咱們最不想看到的,對於用戶而言,立刻就能產生一種不悅,對於投資方而言,也會產生對技術實力的不信任感,因此,咱們就須要對閃退進行處理,這裏介紹一個不錯的三方:AvoidCrash,寫這個的大大也很牛逼,原文參照這裏git

這個三方能夠處理例如插入空值到字典中或數組中引發的崩潰、數組越界引發的崩潰、unrecognized selector sent to instance等等的崩潰,都能捕獲而且避免閃退。

對於插入空值、越界等,原理比較簡單,就是利用Runtime的方法交換,把普通的插入和取值的方法,替換成安全插入和安全讀取的方法,具體代碼能夠去看源碼。
話很少說,先上效果:
如下是可致使崩潰的代碼:github

NSString *nilStr = nil; NSArray *array = @[@"chenfanfang", nilStr]; 

 

 

 
 
崩潰截圖

如有AvoidCrash來防止崩潰,則不會崩潰,而且會將本來會崩潰狀況的詳細信息打印出來,以下圖:
 

 

防止崩潰的效果

 

效果不錯吧,接下來上使用步驟:數組

  • 集成:
    建議使用cocoapod,僅須要pod AvoidCrash一句話便可。(手動導入的步驟,能夠參照上面所說的原文)。xcode

  • 使用方法:(只要在AppDelegatedidFinishLaunchingWithOptions方法中調用avoidCrash方法,就能夠開始監聽異常。)安全

- (void)avoidCrash { /* * 項目初期不須要對"unrecognized selector sent to instance"錯誤進行處理,由於尚未相關的崩潰的類 * 後期出現後,再使用makeAllEffective方法,把全部對應崩潰的類添加到數組中,避免崩潰 * 對於正式線能夠啓用該方法,測試線建議關閉該方法 */ [AvoidCrash becomeEffective]; // [AvoidCrash makeAllEffective]; // NSArray *noneSelClassStrings = @[ // @"NSString" // ]; // [AvoidCrash setupNoneSelClassStringsArr:noneSelClassStrings]; //監聽通知:AvoidCrashNotification, 獲取AvoidCrash捕獲的崩潰日誌的詳細信息 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(dealwithCrashMessage:) name:AvoidCrashNotification object:nil]; } 
  • 再監聽異常的通知:
- (void)dealwithCrashMessage:(NSNotification *)notification { MYLog(@"\n🚫\n🚫監測到崩潰信息🚫\n🚫\n"); /* * 在這邊對避免的異常進行一些處理,好比上傳到日誌服務器等。 */ } 
以上就是避免崩潰的簡單用法,關於能處理哪些異常,能夠自行查看Git中的項目介紹。

2、關於異常的統計

上述的方法,可以避免崩潰,可是不可以避免全部情況的崩潰,做者也在不斷的根據用戶的使用狀況進行更新,儘可能對全部已知的崩潰進行避免。因此,咱們還須要對異常進行其餘的收集,也能有效的幫助本身改進APP。服務器

這裏僅作騰訊的Bugly進行介紹,由於其餘的例如友盟、極光的,我的感受都沒有Bugly好用,我也就不作介紹了,有興趣的能夠自行了解。
這裏參照的文章原文在此app

  • 集成
    集成很簡單,按照官方文檔來就好,咱們這裏建個簡單的小項目,模擬一些崩潰,測試下Bugly的bug上報及時性。
    項目就取名叫NSException了,建立好項目後,去Bugly的控制檯,添加咱們的應用。
     
    添加應用

    建立完應用,進入下一個界面,咱們選擇異常上報。
     

     

     
    選擇異常上報

    再到咱們的APP的appDelegatedidFinishLaunchingWithOptions方法內調用初始化方法便可。
// 頭文件 #import <Bugly/Bugly.h> - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [Bugly startWithAppId:@"此處替換爲你的AppId"]; return YES; } 

AppID能夠點擊你在控制檯建立的App,而後點產品設置就能看到了。測試


 

 

查看appid
  • Bug上傳測試
    接下來咱們在ViewConroller中隨便創造一個閃退的bug
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { NSArray *arr = @[@"", @""]; arr[5]; } 

運行,崩潰,刷新Bugly的控制檯,你會發現,bug已經統計到了。因此,Bugly的崩潰上傳是在崩潰後馬上上傳的。優化


 

 

控制檯記錄的崩潰信息

咱們點進異常問題中去看一下,崩潰信息大體是這樣的,咱們能夠很直觀的看到崩在哪一個方法裏了。ui


 

 

異常信息

進階

若是咱們就這樣使用Bugly是否是太惋惜了,咱們來看看Bugly還有什麼功能;查看頭文件,會發現Bugly有三個類暴露出來,分別是BuglyBuglyConfigBuglyLog

1.BuglyConfig類主要用於個性話配置Bugly類,由一些屬性和BuglyDelegate代理組成。
  • 屬性:BuglyConfig大部分屬性有設有默認值,通常不用更改,可是關於卡頓監控的屬性確是默認關閉的:
/** * 卡頓監控開關,默認關閉 */ @property (nonatomic) BOOL blockMonitorEnable; /** * 卡頓監控判斷間隔,單位爲秒 */ @property (nonatomic) NSTimeInterval blockMonitorTimeout; 

若是須要上報卡頓,只須要將blockMonitorEnable設爲true,給blockMonitorTimeout設置一個合理的值便可;

  • 代理:BuglyConfig能夠設置一個代理,來自定義上傳崩潰的附屬信息;
@protocol BuglyDelegate <NSObject> @optional /** * 發生異常時回調 * @param exception 異常信息 * @return 返回需上報記錄,隨異常上報一塊兒上報 */ - (NSString * BLY_NULLABLE)attachmentForException:(NSException * BLY_NULLABLE)exception; @end 

咱們的初始化就改爲:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { BuglyConfig *config = [[BuglyConfig alloc] init]; //監聽卡頓 config.blockMonitorEnable = YES; config.blockMonitorTimeout = 3; config.consolelogEnable = YES; config.delegate = self; [Bugly startWithAppId:@"此處替換爲你的AppId" config:config]; // [self avoidCrash]; return YES; } - (NSString *)attachmentForException:(NSException *)exception { NSLog(@"異常事件代理"); return [NSString stringWithFormat:@"TEST: %@",exception.userInfo]; } 

再運行一次,崩潰,而後咱們看看Bugly控制檯上報記錄:


 

 

留意紅色邊框內的文件

文件內部記錄的就是異常的代理方法所上報的內容。

2.上傳打印日誌,BuglyLog類主要用於打印日誌,有6種級別:
typedef NS_ENUM(NSUInteger, BuglyLogLevel) { BuglyLogLevelSilent = 0, BuglyLogLevelError = 1, BuglyLogLevelWarn = 2, BuglyLogLevelInfo = 3, BuglyLogLevelDebug = 4, BuglyLogLevelVerbose = 5, }; 

BuglyLog除了控制檯打印,還有一個重要功能就是上報打印內容,內容將在崩潰時一同被上報;可是這個功能是默認不上報的,須要配置BuglyConfig的reportLogLevel屬性;如config.reportLogLevel = BuglyLogLevelWarn,將會上報BuglyLogLevelWarn和BuglyLogLevelError級別的打印日誌。

3.自定義上報異常,咱們再回到Bugly類,除了初始化Bugly的方法外,還有一些其餘的方法:

自定義上報錯誤

/** * 上報自定義異常 * @param exception 異常信息 */ + (void)reportException:(nonnull NSException *)exception; /** * 上報錯誤 * @param error 錯誤信息 */ + (void)reportError:(NSError *)error; 

重點來了!!!!

配合上AvoidCrash,使用上報自定義異常方法,咱們就既能避免崩潰,又能監聽異常!

//AvoidCrash異常通知監聽方法,在這裏咱們能夠調用reportException方法進行上報 - (void)dealwithCrashMessage:(NSNotification *)notification { NSLog(@"\n🚫\n🚫監測到崩潰信息🚫\n🚫\n"); NSException *exception = [NSException exceptionWithName:@"AvoidCrash" reason:[notification valueForKeyPath:@"userInfo.errorName"] userInfo:notification.userInfo]; [Bugly reportException:exception]; } 

以上就是AvoidCrash+Bugly優化APP的運行處理。

雖然Bugly的崩潰列表中咱們能看到獲得代碼的崩潰信息,但想更具體的分析代碼位置,就要用到符號表了。

3、符號表

沒有符號表,咱們就沒法定位崩潰中的符號對應的代碼所在的類以及類中的行數位置。咱們在每次構建版本、debug的時候,都會生成dSYM後綴名的符號表文件,而咱們App在手機上運行的時候,崩潰後產生的崩潰信息,不可能定位到代碼的多少多少行,由於這些信息對於App運行是沒有意義的,存儲在App中勢必會增大安裝包的體積,因此App的崩潰信息都是存儲爲各類符號,具體符號表明什麼,須要去符號表中查找對應的含義。
咱們每次debug、構建版本,都會生成dSYM文件,都對應了一個UUID(像咱們的手機同樣,都有一個惟一標誌),按下圖指示,咱們就能找到咱們所使用的App版本對應的dSYM文件的UUID,經過這個UUID,咱們就能找到存儲在咱們電腦中的dSYM文件,將這個文件上傳到bugly,bugly會自動幫咱們找到崩潰符號的含義。

 

 

 

查看符號表文件

須要注意的是,構建版本會自動生成dSYM文件,但debug的時候,是沒有的,須要咱們手動開啓。在build setting中搜索debug,將下面兩項內容修改成正確的設置:
 

 

手動開始debug模式下的生成dSYM文件

有了符號表的UUID,咱們打開終端,按UUID找到符號表的路徑。
mdfind "com_apple_xcode_dsym_uuids == A8E87810-70A7-3335-B638-C8B01BE15D79"
後面的一串字母數字組合,就是咱們的UUID,這裏須要將UUID按必定格式處理下,也就是在特定位置插入「-」,具體格式以下:
 
處理UUID

來到終端,運行上面的命令,就定位到了dSYM文件的位置:
 

 

定位dSYM文件

打開文件路徑,就找到了dSYM文件:
 

 

找到dSYM文件

拷貝出來,壓縮爲zip文件,上傳到bugly上。
 

 

上傳文件

刷新頁面,再回去看剛纔的問題,定位到了爲ViewController.m的第24行:
 

 

從新查看異常

這樣咱們就定位到了有問題的地方。

 

官網文檔也提供了自動上傳dSYM文件的操做流程,有興趣的能夠試試,避免之後每次新版本都要手動上傳dSYM文件。

dSYM文件也能夠手動查找:


 

 

手動查找dSYM文件

找到你構建的版本,右鍵show in finder:


 

 

查看finder中路徑

而後在定位到的文件上右鍵顯示包內容就OK了:


 

 

包內查找dSYM文件
相關文章
相關標籤/搜索