IOS系統閃退異常(Crash)捕獲處理

測試萌萌發表於2020-12-26

我們的程式經常出現異常造成閃退的現象,對於已經發布的APP,如何捕捉到這些異常,及時進行更新解決閃退,提高體驗感呢?
對於一些簡單,比如一些後臺資料的處理,容易重現陣列越界,字典空指標錯誤的,我們用oc的runtime方法進行捕獲。比如NSArray的陣列越界問題。
原始碼地址:GitHub地址

//
//  ViewController.m
//  CatchCrash
//
//  Created by Sem on 2020/8/28.
//  Copyright © 2020 SEM. All rights reserved.
//

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
   
    // Do any additional setup after loading the view.
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    NSArray *dd =@[@"1",@"2"];
       NSString *z =dd[3];
       NSLog(@"~~~~~%@",z);
}
@end

我們可以通過runtime進行方法替換,比如我們捕獲NSArray的陣列越界問題,注意NSArray 是個類簇所以不能簡單新增類目

+(BOOL)SQ_HookOriInstanceMethod:(SEL)oriSel NewInstanceMethod:(SEL)newSel{
    Class class = objc_getRequiredClass("__NSArrayI");
    Method origMethod = class_getInstanceMethod(class, oriSel);
    Method newMethod = class_getInstanceMethod(self, newSel);
    if(!origMethod||!newMethod){
        return NO;
    }
    method_exchangeImplementations(origMethod, newMethod);
    return YES;
    
}
-(id)objectAtIndexedSubscriptNew:(NSUInteger)index{
    if(index>=self.count){
        //程式碼處理 上傳伺服器.
        return nil;
    }
    return [self objectAtIndexedSubscriptNew:index] ;
}

當然這種捕獲只能捕獲單一的問題,還有其他的報錯,那就要寫很多的分類處理,如何進行統一的捕捉呢,我們檢視下報錯資訊看下能不找到有用的資訊。

如圖我們看了報錯的方法棧。看到有libobjc的呼叫。這個就很熟悉了,去看下runtime的原始碼。可以找到set_terminate設定中止的回撥,也就是如果出現報錯,系統會回撥這個函式,如果外界沒有傳這個函式objc_setUncaightExceptionHandler,系統會使用預設的實現。 我們只要呼叫NSSetUncaughtExceptionHandler就可以設定這個方法控制程式碼,系統出現報錯時候,回撥這個方法,從而讓我們對這個錯誤進行處理.
在AppDelegate裡面設定這個方法控制程式碼

NSSetUncaughtExceptionHandler(&HandleException);

然後就可以捕捉異常 ,上傳服務或者儲存在本地。

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] autorelease]
        performSelectorOnMainThread:@selector(handleException:)
        withObject:
            [NSException
                exceptionWithName:[exception name]
                reason:[exception reason]
                userInfo:userInfo]
        waitUntilDone:YES];
}

然後在這個物件中通過runloop,保住執行緒,處理後再崩潰.

如果你

①從事功能測試,想進階自動化測試

②在測試界混了1、2年,依然不會敲程式碼

③面試大廠卻屢屢碰壁

我推薦一個學習交流群!來吧~~測試員,313782132(Q群裡有技術大牛一起交流分享,學習資源的價值取決於你的行動,莫做“收藏家”)獲取更多大廠技術、面試資料~

最後:

凡事要趁早,特別是技術行業,一定要提升技術功底,豐富自動化專案實戰經驗,這對於你未來幾年職業規劃,以及測試技術掌握的深度非常有幫助。

相關文章