iOS中如何防止程序crash

1.程序中常見的crash種類有

  • 1.unSelector 方法未找到
  • 2.KVO未移除稠歉,多次添加箱蝠,多次移除問題
  • 3.數(shù)組越界
  • 4.字典賦值key或value為nil
  • 5.NSString substringFromIndex 越界問題
  • 6.NSAttributedString initWithString stirng=nil問題
  • 7.通知deallooc時為移除問題

2.解決方案

1.unSelector可以利用消息轉(zhuǎn)發(fā)來解決

消息轉(zhuǎn)發(fā)有三種時機(jī)

  • 1.在resolveInstanceMethod方法里续捂,添加方法,但是此種方式損害了原有類的結(jié)構(gòu)
+(BOOL)resolveInstanceMethod:(SEL)sel
{
    return YES;
}
  • 2.在下面方法里放回一個自定義對象,同時給此對象添加未實(shí)現(xiàn)的方法
-(id)forwardingTargetForSelector:(SEL)aSelector
{
    rerurn obj;
}

3.完整轉(zhuǎn)發(fā)宦搬,對方法進(jìn)行簽名牙瓢,自定義對象然后invoke

  • 這里采用第3種方式,生成方法簽名床三,然后forwardInvocation一罩,這里self invoke,因?yàn)閟elf沒有此方法會捕獲到異常撇簿,避免程序崩潰
[self safe_exchangeInstanceMethod:[self class] originalSel:@selector(methodSignatureForSelector:) newSel:@selector(safe_methodSignatureForSelector:)];        
[self safe_exchangeInstanceMethod:[self class] originalSel:@selector(forwardInvocation:) newSel:@selector(safe_forwardInvocation:)];
- (NSMethodSignature *)safe_methodSignatureForSelector:(SEL)aSelector
{
    NSMethodSignature *ms = [self safe_methodSignatureForSelector:aSelector];
    if ([self respondsToSelector:aSelector] || ms){
        return ms;
    }
    else{
        return [LSSafeProxy instanceMethodSignatureForSelector:@selector(safe_crashLog)];
    }
}

- (void)safe_forwardInvocation:(NSInvocation *)anInvocation{
    @try {
        [self safe_forwardInvocation:anInvocation];
        
    } @catch (NSException *exception) {
        LSSafeProtectionCrashLog(exception,LSSafeProtectorCrashTypeSelector);
    } @finally {
    }
}
2.KVO未移除聂渊,多次添加差购,多次移除,observeValueForKeyPath:ofObject:change:context:未實(shí)現(xiàn)
  • 1.這里交換NSObject以下方法
[self safe_exchangeInstanceMethod:[self class] originalSel:@selector(addObserver:forKeyPath:options:context:) newSel:@selector(safe_addObserver:forKeyPath:options:context:)];
[self safe_exchangeClassMethod:[self class] originalSel:@selector(observeValueForKeyPath:ofObject:change:context:) newSel:@selector(safe_observeValueForKeyPath:ofObject:change:context:)];
[self safe_exchangeInstanceMethod:[self class] originalSel:@selector(removeObserver:forKeyPath:) newSel:@selector(safe_removeObserver:forKeyPath:)];
[self safe_exchangeInstanceMethod:[self class] originalSel:@selector(removeObserver:forKeyPath:context:) newSel:@selector(safe_removeObserver:forKeyPath:context:)];

用safe_downObservedKeyPathArray記錄有哪些人監(jiān)聽了自己汉嗽,用safe_upObservedArray記錄自己監(jiān)聽了哪些對象欲逃,用來在addObserver時判斷是否添加過了,removeObserver時是否已經(jīng)移除過了饼暑,或者壓根沒有添加過不能移除稳析,在dealloc時自動移除,交換observeValueForKeyPath:ofObject:change:context方法避免監(jiān)聽者未實(shí)現(xiàn)此方法

3.數(shù)組越界 弓叛,交換數(shù)組的一些方法彰居,需要注意的就是數(shù)組有類簇,所以需要交換多個類,根據(jù)以下規(guī)則交換方法即可
// < = iOS 8:下都是__NSArrayI
//iOS9 @[] 是__NSArray0  @[@"fd"]是__NSArrayI
//iOS10以后(含10): 分 __NSArrayI撰筷、  __NSArray0陈惰、__NSSingleObjectArrayI


//__NSArrayM   NSMutableArray創(chuàng)建的都為__NSArrayM
//__NSArray0   除__NSArrayM 0個元素都為__NSArray0
// __NSSingleObjectArrayI @[@"fds"]只有此形式創(chuàng)建而且僅一個元素為__NSSingleObjectArrayI
//__NSArrayI   @[@"fds",@"fsd"]方式創(chuàng)建多于1個元素 或者 arrayWith創(chuàng)建都是__NSArrayI



//__NSArray0
//arr@[11]   調(diào)用的是  [__NSArray0 objectAtIndex:]

//__NSSingleObjectArrayI
//arr@[11] 調(diào)用的是  [__NSSingleObjectArrayI objectAtIndex:]

//不可變數(shù)組
// <  iOS11: arr@[11]  調(diào)用的是[__NSArrayI objectAtIndex:]
// >= iOS11: arr@[11]  調(diào)用的是[__NSArrayI objectAtIndexedSubscript]
//  任意系統(tǒng)   [arr objectAtIndex:111]  調(diào)用的都是[__NSArrayM objectAtIndex:]

//可變數(shù)組
// <  iOS11: arr@[11]  調(diào)用的是[__NSArrayM objectAtIndex:]
// >= iOS11: arr@[11]  調(diào)用的是[__NSArrayM objectAtIndexedSubscript]
//  任意系統(tǒng)   [arr objectAtIndex:111]  調(diào)用的都是[__NSArrayI objectAtIndex:]
[self safe_exchangeInstanceMethod:objc_getClass("__NSPlaceholderArray") originalSel:@selector(initWithObjects:count:) newSel:@selector(safe_initWithObjects:count:)];  
[self safe_exchangeInstanceMethod:objc_getClass("__NSArrayI") originalSel:@selector(objectAtIndex:) newSel:@selector(safe_objectAtIndexI:)];
[self safe_exchangeInstanceMethod:objc_getClass("__NSArrayI") originalSel:@selector(objectAtIndexedSubscript:) newSel:@selector(safe_objectAtIndexedSubscriptI:)];
[self safe_exchangeInstanceMethod:objc_getClass("__NSArray0") originalSel:@selector(objectAtIndex:) newSel:@selector(safe_objectAtIndex0:)];
[self safe_exchangeInstanceMethod:objc_getClass("__NSSingleObjectArrayI") originalSel:@selector(objectAtIndex:) newSel:@selector(safe_objectAtIndexSI:)];
   //方法交換只要一次就好
   Class dClass=NSClassFromString(@"__NSArrayM");
   //由于低于11.0交換此方法會導(dǎo)致有鍵盤顯示的地方,此時退到后臺會crash [UIKeyboardLayoutStar release]: message sent to deallocated instance 0x7fd762cc11f0
     if ([UIDevice currentDevice].systemVersion.doubleValue>=11.0) {
          [self safe_exchangeInstanceMethod:dClass originalSel:@selector(objectAtIndex:) newSel:@selector(safe_objectAtIndexM:)];
       }
      //因?yàn)?1.0以上系統(tǒng)才會調(diào)用此方法毕籽,所以大于11.0才交換此方法
     if([UIDevice currentDevice].systemVersion.doubleValue>=11.0){
            [self safe_exchangeInstanceMethod:dClass originalSel:@selector(objectAtIndexedSubscript:) newSel:@selector(safe_objectAtIndexedSubscriptM:)];
        }
        
      [self safe_exchangeInstanceMethod:dClass originalSel:@selector(insertObject:atIndex:) newSel:@selector(safe_insertObject:atIndex:)];
      [self safe_exchangeInstanceMethod:dClass originalSel:@selector(removeObjectAtIndex:) newSel:@selector(safe_removeObjectAtIndex:)];
      [self safe_exchangeInstanceMethod:dClass originalSel:@selector(replaceObjectAtIndex:withObject:) newSel:@selector(safe_replaceObjectAtIndex:withObject:)];
4.字典賦值key或value為nil
/*
 大概和NSArray類似  也是iOS8之前都是__NSDictionaryI抬闯,其他的參考NSArray
 
 __NSSingleEntryDictionaryI
 @{@"key":@"value"} 此種形式創(chuàng)建而且僅一個可以為__NSSingleEntryDictionaryI
 __NSDictionaryM
 NSMutableDictionary創(chuàng)建都為__NSDictionaryM
 __NSDictionary0
 除__NSDictionaryM外 不管什么方式創(chuàng)建0個key都為__NSDictionary0
 __NSDictionaryI
 @{@"key":@"value",@"key2",@"value2"}此種方式創(chuàng)建多于1個key,或者initWith創(chuàng)建都是__NSDictionaryI

 NSMutableDictionary通過下標(biāo)方式賦值的時候关筒,value為nil不會崩潰
    iOS11之前會調(diào)用 setObject:forKey
    iOS11之后(含11)  setObject:forKeyedSubscript:
 */
 [self safe_exchangeInstanceMethod:NSClassFromString(@"__NSPlaceholderDictionary") originalSel:@selector(initWithObjects:forKeys:count:) newSel:@selector(safe_initWithObjects:forKeys:count:)];
 [self safe_exchangeInstanceMethod:NSClassFromString(@"__NSPlaceholderDictionary") originalSel:@selector(initWithObjects:forKeys:) newSel:@selector(safe_initWithObjects:forKeys:)]; 
   Class dClass=NSClassFromString(@"__NSDictionaryM");
   [self safe_exchangeInstanceMethod:dClass originalSel:@selector(setObject:forKey:) newSel:@selector(safe_setObject:forKey:)];
   [self safe_exchangeInstanceMethod:dClass originalSel:@selector(setObject:forKeyedSubscript:) newSel:@selector(safe_setObject:forKeyedSubscript:)];
   [self safe_exchangeInstanceMethod:dClass originalSel:@selector(removeObjectForKey:) newSel:@selector(safe_removeObjectForKey:)];
5.NSString substringFromIndex 越界問題
        Class dClass=NSClassFromString(@"__NSCFConstantString");
        Class NSPlaceholderStringClass=NSClassFromString(@"NSPlaceholderString");
        
        //initWithString:
        [self safe_exchangeInstanceMethod:NSPlaceholderStringClass originalSel:@selector(initWithString:) newSel:@selector(safe_initWithString:)];
        
        //hasPrefix
        [self safe_exchangeInstanceMethod:dClass originalSel:@selector(hasPrefix:) newSel:@selector(safe_hasPrefix:)];
        
       //hasSuffix
        [self safe_exchangeInstanceMethod:dClass originalSel:@selector(hasSuffix:) newSel:@selector(safe_hasSuffix:)];
        
        //substringFromIndex
        [self safe_exchangeInstanceMethod:dClass originalSel:@selector(substringFromIndex:) newSel:@selector(safe_substringFromIndex:)];
        
        //substringToIndex
        [self safe_exchangeInstanceMethod:dClass originalSel:@selector(substringToIndex:) newSel:@selector(safe_substringToIndex:)];
        
        //substringWithRange
         [self safe_exchangeInstanceMethod:dClass originalSel:@selector(substringWithRange:) newSel:@selector(safe_substringWithRange:)];
        
        //characterAtIndex
         [self safe_exchangeInstanceMethod:dClass originalSel:@selector(characterAtIndex:) newSel:@selector(safe_characterAtIndex:)];
        
         //stringByReplacingOccurrencesOfString:withString:options:range:
        [self safe_exchangeInstanceMethod:dClass originalSel:@selector(stringByReplacingOccurrencesOfString:withString:options:range:) newSel:@selector(safe_stringByReplacingOccurrencesOfString:withString:options:range:)];
        
        //stringByReplacingCharactersInRange:withString:
         [self safe_exchangeInstanceMethod:dClass originalSel:@selector(stringByReplacingCharactersInRange:withString:) newSel:@selector(safe_stringByReplacingCharactersInRange:withString:)];
  
        Class dClass=NSClassFromString(@"__NSCFString");
        Class NSPlaceholderMutableStringClass=NSClassFromString(@"NSPlaceholderMutableString");
        
        //initWithString:
        [self safe_exchangeInstanceMethod:NSPlaceholderMutableStringClass originalSel:@selector(initWithString:) newSel:@selector(safe_initWithString:)];
        
        
        //hasPrefix
        [self safe_exchangeInstanceMethod:dClass originalSel:@selector(hasPrefix:) newSel:@selector(safe_hasPrefix:)];
        
        //hasSuffix
        [self safe_exchangeInstanceMethod:dClass originalSel:@selector(hasSuffix:) newSel:@selector(safe_hasSuffix:)];
        
        
        //substringFromIndex
        [self safe_exchangeInstanceMethod:dClass originalSel:@selector(substringFromIndex:) newSel:@selector(safe_substringFromIndex:)];
        
        //substringToIndex
        [self safe_exchangeInstanceMethod:dClass originalSel:@selector(substringToIndex:) newSel:@selector(safe_substringToIndex:)];
        
        //substringWithRange
        [self safe_exchangeInstanceMethod:dClass originalSel:@selector(substringWithRange:) newSel:@selector(safe_substringWithRange:)];
        
        //characterAtIndex
        [self safe_exchangeInstanceMethod:dClass originalSel:@selector(characterAtIndex:) newSel:@selector(safe_characterAtIndex:)];
        
        
        //stringByReplacingOccurrencesOfString:withString:options:range:
        [self safe_exchangeInstanceMethod:dClass originalSel:@selector(stringByReplacingOccurrencesOfString:withString:options:range:) newSel:@selector(safe_stringByReplacingOccurrencesOfString:withString:options:range:)];
        
        
        //stringByReplacingCharactersInRange:withString:
        [self safe_exchangeInstanceMethod:dClass originalSel:@selector(stringByReplacingCharactersInRange:withString:) newSel:@selector(safe_stringByReplacingCharactersInRange:withString:)];
        
        
        
        //replaceCharactersInRange:withString:
        [self safe_exchangeInstanceMethod:dClass originalSel:@selector(replaceCharactersInRange:withString:) newSel:@selector(safe_replaceCharactersInRange:withString:)];
        
        //replaceOccurrencesOfString:withString:options:range:
        [self safe_exchangeInstanceMethod:dClass originalSel:@selector(replaceOccurrencesOfString:withString:options:range:) newSel:@selector(safe_replaceOccurrencesOfString:withString:options:range:)];
        
        //insertString:atIndex:
        [self safe_exchangeInstanceMethod:dClass originalSel:@selector(insertString:atIndex:) newSel:@selector(safe_insertString:atIndex:)];
        
        //deleteCharactersInRange:
        [self safe_exchangeInstanceMethod:dClass originalSel:@selector(deleteCharactersInRange:) newSel:@selector(safe_deleteCharactersInRange:)];
        
        
        //appendString:
        [self safe_exchangeInstanceMethod:dClass originalSel:@selector(appendString:) newSel:@selector(safe_appendString:)];
        
        
        //setString:
        [self safe_exchangeInstanceMethod:dClass originalSel:@selector(setString:) newSel:@selector(safe_setString:)];
        
6.NSAttributedString initWithString stirng=nil問題
       Class dClass = NSClassFromString(@"NSConcreteAttributedString");
        
        //initWithString:
        [self safe_exchangeInstanceMethod:dClass originalSel:@selector(initWithString:) newSel:@selector(safe_initWithString:)];
        
        //initWithAttributedString
        [self safe_exchangeInstanceMethod:dClass originalSel:@selector(initWithString:attributes:) newSel:@selector(safe_initWithString:attributes:)];
        
        //initWithString:attributes:
        [self safe_exchangeInstanceMethod:dClass originalSel:@selector(initWithAttributedString:) newSel:@selector(safe_initWithAttributedString:)];
       Class dClass = NSClassFromString(@"NSConcreteMutableAttributedString");
        
        //initWithString:
        [self safe_exchangeInstanceMethod:dClass originalSel:@selector(initWithString:) newSel:@selector(safe_initWithString:)];
        
        //initWithAttributedString
        [self safe_exchangeInstanceMethod:dClass originalSel:@selector(initWithString:attributes:) newSel:@selector(safe_initWithString:attributes:)];
        
        //initWithString:attributes:
        [self safe_exchangeInstanceMethod:dClass originalSel:@selector(initWithAttributedString:) newSel:@selector(safe_initWithAttributedString:)];
        
        
        //以下為NSMutableAttributedString特有方法
        //4.replaceCharactersInRange:withString:
        [self safe_exchangeInstanceMethod:dClass originalSel:@selector(replaceCharactersInRange:withString:) newSel:@selector(safe_replaceCharactersInRange:withString:)];
        
        //5.setAttributes:range:
        [self safe_exchangeInstanceMethod:dClass originalSel:@selector(setAttributes:range:) newSel:@selector(safe_setAttributes:range:)];
        
        
        
        //6.addAttribute:value:range:
        [self safe_exchangeInstanceMethod:dClass originalSel:@selector(addAttribute:value:range:) newSel:@selector(safe_addAttribute:value:range:)];
        
        //7.addAttributes:range:
        [self safe_exchangeInstanceMethod:dClass originalSel:@selector(addAttributes:range:) newSel:@selector(safe_addAttributes:range:)];
        
        //8.removeAttribute:range:
        [self safe_exchangeInstanceMethod:dClass originalSel:@selector(removeAttribute:range:) newSel:@selector(safe_removeAttribute:range:)];
        
        //9.replaceCharactersInRange:withAttributedString:
        [self safe_exchangeInstanceMethod:dClass originalSel:@selector(replaceCharactersInRange:withAttributedString:) newSel:@selector(safe_replaceCharactersInRange:withAttributedString:)];
        
        
        //10.insertAttributedString:atIndex:
        [self safe_exchangeInstanceMethod:dClass originalSel:@selector(insertAttributedString:atIndex:) newSel:@selector(safe_insertAttributedString:atIndex:)];
        
        
        //11.appendAttributedString:
        [self safe_exchangeInstanceMethod:dClass originalSel:@selector(appendAttributedString:) newSel:@selector(safe_appendAttributedString:)];
        
        //12.deleteCharactersInRange:
        [self safe_exchangeInstanceMethod:dClass originalSel:@selector(deleteCharactersInRange:) newSel:@selector(safe_deleteCharactersInRange:)];
        
        //13.setAttributedString:
        [self safe_exchangeInstanceMethod:dClass originalSel:@selector(setAttributedString:) newSel:@selector(safe_setAttributedString:)];
        
7.通知deallooc時為移除問題

dealloc時不管通知中心添沒添加自己溶握,都移除

此框架可以捕獲到異常,以及未移除的kvo蒸播,打印出信息睡榆,回調(diào)給用戶,用戶可以上傳到bugly等crash統(tǒng)計平臺

//注意線上環(huán)境isDebug一定要設(shè)置為NO)
[LSSafeProtector openSafeProtectorWithIsDebug:YES block:^(NSException *exception, LSSafeProtectorCrashType crashType) {
//[Bugly reportException:exception];

//此方法相對于上面的方法袍榆,好處在于bugly后臺查看bug崩潰位置時肉微,不用點(diǎn)擊跟蹤數(shù)據(jù),再點(diǎn)擊crash_attach.log蜡塌,查看里面的額外信息來查看崩潰位置
[Bugly reportExceptionWithCategory:3 name:exception.name reason:[NSString stringWithFormat:@"%@  崩潰位置:%@",exception.reason,exception.userInfo[@"location"]] callStack:@[exception.userInfo[@"callStackSymbols"]] extraInfo:exception.userInfo terminateApp:NO];
}];

具體代碼demo請查看github https://github.com/lsmakethebest/LSSafeProtector

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市勿负,隨后出現(xiàn)的幾起案子馏艾,更是在濱河造成了極大的恐慌,老刑警劉巖奴愉,帶你破解...
    沈念sama閱讀 210,978評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件琅摩,死亡現(xiàn)場離奇詭異,居然都是意外死亡锭硼,警方通過查閱死者的電腦和手機(jī)房资,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評論 2 384
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來檀头,“玉大人轰异,你說我怎么就攤上這事岖沛。” “怎么了搭独?”我有些...
    開封第一講書人閱讀 156,623評論 0 345
  • 文/不壞的土叔 我叫張陵婴削,是天一觀的道長。 經(jīng)常有香客問我牙肝,道長唉俗,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,324評論 1 282
  • 正文 為了忘掉前任配椭,我火速辦了婚禮虫溜,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘股缸。我一直安慰自己衡楞,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,390評論 5 384
  • 文/花漫 我一把揭開白布乓序。 她就那樣靜靜地躺著寺酪,像睡著了一般。 火紅的嫁衣襯著肌膚如雪替劈。 梳的紋絲不亂的頭發(fā)上寄雀,一...
    開封第一講書人閱讀 49,741評論 1 289
  • 那天,我揣著相機(jī)與錄音陨献,去河邊找鬼盒犹。 笑死,一個胖子當(dāng)著我的面吹牛眨业,可吹牛的內(nèi)容都是我干的急膀。 我是一名探鬼主播,決...
    沈念sama閱讀 38,892評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼龄捡,長吁一口氣:“原來是場噩夢啊……” “哼卓嫂!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起聘殖,我...
    開封第一講書人閱讀 37,655評論 0 266
  • 序言:老撾萬榮一對情侶失蹤晨雳,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后奸腺,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體餐禁,經(jīng)...
    沈念sama閱讀 44,104評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年突照,在試婚紗的時候發(fā)現(xiàn)自己被綠了帮非。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,569評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖末盔,靈堂內(nèi)的尸體忽然破棺而出筑舅,到底是詐尸還是另有隱情,我是刑警寧澤庄岖,帶...
    沈念sama閱讀 34,254評論 4 328
  • 正文 年R本政府宣布豁翎,位于F島的核電站,受9級特大地震影響隅忿,放射性物質(zhì)發(fā)生泄漏心剥。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,834評論 3 312
  • 文/蒙蒙 一背桐、第九天 我趴在偏房一處隱蔽的房頂上張望优烧。 院中可真熱鬧,春花似錦链峭、人聲如沸畦娄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽熙卡。三九已至,卻和暖如春励饵,著一層夾襖步出監(jiān)牢的瞬間驳癌,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評論 1 264
  • 我被黑心中介騙來泰國打工役听, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留颓鲜,地道東北人。 一個月前我還...
    沈念sama閱讀 46,260評論 2 360
  • 正文 我出身青樓典予,卻偏偏與公主長得像甜滨,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子瘤袖,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,446評論 2 348