WKCrashSDK - crash攔截工具

前言

由于線上始終出現(xiàn)部分未知原因崩潰問題,遂遵循網(wǎng)易出的crash攔截機(jī)制,自實現(xiàn)了一個crash攔截工具,現(xiàn)已上線運(yùn)行數(shù)月,累計攔截閃退···總之很多啦···


5.png

實現(xiàn)原理

原理網(wǎng)上已有很多文章闡述,這里推薦幾個鏈接。

網(wǎng)易iOS App運(yùn)行時Crash自動防護(hù)實踐
黑魔法教你讓iOS APP防住Crash

優(yōu)勢:

  • 封裝完善,使用方便,僅需將文件導(dǎo)入項目即可生效。
  • 具備debug期crash發(fā)生的UI層級提示。
  • 可和線上接口配合實現(xiàn)實時開關(guān)操作。
  • 可自定crashinfo上傳地點(我司是直接上傳到bugly搜集)
  • 經(jīng)過實際測試,已在我司多個線上APP實測有效,暫未發(fā)現(xiàn)有什么奇怪的問題。

項目要點

其實從上述原理文章以及能夠了解基本的實現(xiàn)邏輯,只是在實現(xiàn)過程中也遇到了不少的坑。下面就和大家分享一下一些實現(xiàn)過程的坑以及為了滿足我司需求拓展的一些功能點。

  • KVO

1、攔截KVO時,存在部分三方庫的不能攔截,以及系統(tǒng)的相機(jī)相冊無需攔截,否則會出現(xiàn)無效的crash提示,在我的項目已經(jīng)進(jìn)行了白名單過濾。如果用了一些特殊的三方,可能在使用此工具時,需要收錄一下,避免無效的才讓身體是被收集。

//白名單主要針對觀察者,因為被觀察者很有可能是系統(tǒng)類,所以只能針對觀察者處理,如果攔截到系統(tǒng)的觀察者,則記錄入白名單
+ (NSArray *)kvoWhiteList
{
    static NSArray *whiteList = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        whiteList = @[@"WKKVOProxy",//自己的
                      @"RACKVOProxy",//RAC的
                      @"BLYSDKManager",//bugly的
                      @"_YYTextKeyboardViewFrameObserver",//YYKit的
                      //相冊相關(guān)
                      @"PLManagedAlbum",
                      @"AVCapturePhotoOutput",
                      @"AVCaptureStillImageOutput",
                      //3.2.9添加 拍照相關(guān)
                      @"AVCaptureSession",
                      @"PLPhotoStreamAlbum",
                      @"AVKVODispatcher",
                      @"PLCloudSharedAlbum",
                      @"AVPlayerPropertyCache",
                      ];//@"AVCaptureFigVideoDevice"
    });
    return whiteList;
}

2、對KVO的攔截,需使用遞歸鎖保證線程安全。

        wk_pthread_mutex_init_recursive(&_lock,true);
        pthread_mutex_lock(&_lock);
        pthread_mutex_unlock(&_lock);
  • Zombie

在有僵尸對象造成崩潰時,實際是將其數(shù)據(jù)置為空,但是并不釋放它,然后將其isa指向一個可接受任何方法的中轉(zhuǎn)類中,以此來攔截掉崩潰。為了統(tǒng)一處理crash上報,在這里用了動態(tài)類創(chuàng)建傳遞類型信息的方式。并且.m文件需要使用mrl,在編譯處添加-fno-objc-arc即可。

        NSString *className = NSStringFromClass(selfClass);
        NSString *zombieClassName = [@"WKZombie_" stringByAppendingString: className];//這一步很重要,動態(tài)生成類,如果被僵尸,則可以得知實際是哪個類產(chǎn)生了僵尸指針 導(dǎo)致崩潰
        Class zombieClass = NSClassFromString(zombieClassName);
        if(!zombieClass) {
            zombieClass = objc_allocateClassPair([WKZombieStub class], [zombieClassName UTF8String], 0);
        }
        objc_destructInstance(self);//銷毀實例 相關(guān)信息 內(nèi)存不釋放
        object_setClass(self, zombieClass);
        instanceList.size();
        if (instanceList.size() >= maxCount) {
            id object = instanceList.front();
            instanceList.pop_front();
            free(object);
        }
        instanceList.push_back(self);
  • Container

在攔截NSArray以及NSDictionary的系列方法時,需要注意一下它們的實現(xiàn)方式是類簇實現(xiàn),需要找到它們真實的類來攔截才有效。

swizzling_exchangeMethod(objc_getClass("__NSArray0"), @selector(objectAtIndex:), @selector(emptyArray_objectAtIndex:));
swizzling_exchangeMethod(objc_getClass("__NSArrayI"), @selector(objectAtIndex:), @selector(arrayI_objectAtIndex:));
swizzling_exchangeMethod(objc_getClass("__NSSingleObjectArrayI"), @selector(objectAtIndex:), @selector(singleObjectArrayI_objectAtIndex:));

在對NSMutablArray攔截時,需要特別注意其objectAtIndex的方法,需得在遵守MRC的文件下攔截,否則會在iOS8上彈出鍵盤時,APP進(jìn)入后臺產(chǎn)生崩潰。是必現(xiàn)的。所以在工具中 這個方法是單獨放到一個文件里面hook的,然后在編譯處為此文件添加-fno-objc-arc。

  • UI層級提示信息

在Debug模式下,當(dāng)攔截到crash時,會出現(xiàn)UI層級的提示,如下圖:


1.png

點擊按鈕可以查看具體的崩潰信息,如下圖


2.png

前面title表示為崩潰的類型,后面數(shù)字為攔截的次數(shù)。

再次點擊cell可定位崩潰的文件、對應(yīng)方法名、最近一次崩潰發(fā)生的時間以及在本機(jī)上這個崩潰發(fā)生的次數(shù)。


3.png

4.png

大家可能也注意到了Crash的按鈕是可以隨意拖動,以及根據(jù)你進(jìn)入的大類型不同來變更提示信息的。一個可有可無的小優(yōu)化~

  • CrashInfo上報

CrashInfo的收集,我們只需要關(guān)注WKCrashReport類,去實現(xiàn)它的一個代理即可。

@protocol WKCrashReportDelegate <NSObject>

- (void)handleCrashInfo:(WKCrashModel *)model type:(NSString *)type;

@end

返回的兩個參數(shù):WKCrashModel 以及 NSString type其功用如下:

WKCrashModel

@interface WKCrashModel : NSObject
@property (nonatomic, strong) NSString * clasName; //產(chǎn)生crash的類名
@property (nonatomic, strong) NSString * msg; //could be 方法名,或者其他有效信息
@property (nonatomic, strong) NSArray  * threadStack;//crash時的堆棧信息
@property (nonatomic, assign) NSTimeInterval time;//crash時間
@property (nonatomic, strong, readonly) NSString * deviceType;//設(shè)備信息
@property (nonatomic, strong, readonly) NSString * systemVersion;//系統(tǒng)版本
@end

NSString type
其返回值可能有UnrecognizedSelector,KVO,Container,Timer,NotificationCenter,Null,String,Zombie
分別代表八種攔截的crash類型

PS:如有特殊需求可自行擴(kuò)充

使用方式

Demo地址
進(jìn)入Demo地址找到WKCrashManagerDemo里面的WKCrashSDK文件夾,拖入項目即可。
后續(xù)我會抽空將其加入cocoapods豪華午餐~

注:如從Demo中直接拖入,則默認(rèn)開啟除了Zomie攔截外的其他7種類型的crash攔截。如需自定義請查看WKCrashManager的實現(xiàn)文件。

聯(lián)系方式

如有興趣可通過郵箱357863248@qq.com一起交流進(jìn)步。

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

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

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