閃退估計是咱們最不想看到的,對於用戶而言,立刻就能產生一種不悅,對於投資方而言,也會產生對技術實力的不信任感,因此,咱們就須要對閃退進行處理,這裏介紹一個不錯的三方:AvoidCrash
,寫這個的大大也很牛逼,原文參照這裏。git
unrecognized selector sent to instance
等等的崩潰,都能捕獲而且避免閃退。對於插入空值、越界等,原理比較簡單,就是利用Runtime
的方法交換,把普通的插入和取值的方法,替換成安全插入和安全讀取的方法,具體代碼能夠去看源碼。
話很少說,先上效果:
如下是可致使崩潰的代碼:
github
NSString *nilStr = nil; NSArray *array = @[@"chenfanfang", nilStr];
如有AvoidCrash來防止崩潰,則不會崩潰,而且會將本來會崩潰狀況的詳細信息打印出來,以下圖:
效果不錯吧,接下來上使用步驟:數組
集成:
建議使用cocoapod
,僅須要pod AvoidCrash
一句話便可。(手動導入的步驟,能夠參照上面所說的原文)。xcode
使用方法:(只要在AppDelegate
的didFinishLaunchingWithOptions
方法中調用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"); /* * 在這邊對避免的異常進行一些處理,好比上傳到日誌服務器等。 */ }
上述的方法,可以避免崩潰,可是不可以避免全部情況的崩潰,做者也在不斷的根據用戶的使用狀況進行更新,儘可能對全部已知的崩潰進行避免。因此,咱們還須要對異常進行其餘的收集,也能有效的幫助本身改進APP。服務器
這裏僅作騰訊的Bugly進行介紹,由於其餘的例如友盟、極光的,我的感受都沒有Bugly好用,我也就不作介紹了,有興趣的能夠自行了解。
這裏參照的文章原文在此。app
NSException
了,建立好項目後,去Bugly的控制檯,添加咱們的應用。
appDelegate
中didFinishLaunchingWithOptions
方法內調用初始化方法便可。// 頭文件 #import <Bugly/Bugly.h> - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [Bugly startWithAppId:@"此處替換爲你的AppId"]; return YES; }
AppID能夠點擊你在控制檯建立的App,而後點產品設置就能看到了。測試
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { NSArray *arr = @[@"", @""]; arr[5]; }
運行,崩潰,刷新Bugly的控制檯,你會發現,bug已經統計到了。因此,Bugly的崩潰上傳是在崩潰後馬上上傳的。優化
咱們點進異常問題中去看一下,崩潰信息大體是這樣的,咱們能夠很直觀的看到崩在哪一個方法裏了。ui
若是咱們就這樣使用Bugly是否是太惋惜了,咱們來看看Bugly還有什麼功能;查看頭文件,會發現Bugly有三個類暴露出來,分別是Bugly
、BuglyConfig
和BuglyLog
。
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控制檯上報記錄:
文件內部記錄的就是異常的代理方法所上報的內容。
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級別的打印日誌。
自定義上報錯誤
/** * 上報自定義異常 * @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的崩潰列表中咱們能看到獲得代碼的崩潰信息,但想更具體的分析代碼位置,就要用到符號表了。
沒有符號表,咱們就沒法定位崩潰中的符號對應的代碼所在的類以及類中的行數位置。咱們在每次構建版本、debug的時候,都會生成dSYM後綴名的符號表文件,而咱們App在手機上運行的時候,崩潰後產生的崩潰信息,不可能定位到代碼的多少多少行,由於這些信息對於App運行是沒有意義的,存儲在App中勢必會增大安裝包的體積,因此App的崩潰信息都是存儲爲各類符號,具體符號表明什麼,須要去符號表中查找對應的含義。
咱們每次debug、構建版本,都會生成dSYM文件,都對應了一個UUID(像咱們的手機同樣,都有一個惟一標誌),按下圖指示,咱們就能找到咱們所使用的App版本對應的dSYM文件的UUID,經過這個UUID,咱們就能找到存儲在咱們電腦中的dSYM文件,將這個文件上傳到bugly,bugly會自動幫咱們找到崩潰符號的含義。
mdfind "com_apple_xcode_dsym_uuids == A8E87810-70A7-3335-B638-C8B01BE15D79"
官網文檔也提供了自動上傳dSYM文件的操做流程,有興趣的能夠試試,避免之後每次新版本都要手動上傳dSYM文件。
dSYM文件也能夠手動查找:
找到你構建的版本,右鍵show in finder:
而後在定位到的文件上右鍵顯示包內容就OK了: