防崩潰

疑惑解答

很多開發(fā)小伙伴經(jīng)常私信問我一些問題:

?1飒硅、若集成了騰訊Bugly或者友盟等等異常搜集的SDK,AvoidCrash會(huì)影響到它們的異常搜集嗎束析?

?2堡距、為什么集成了AvoidCrash還是會(huì)報(bào)unrecognized selector sent to instance的異常趾断?

關(guān)于疑惑的解答绍申,請(qǐng)點(diǎn)擊[AvoidCrash疑惑解答](http://www.reibang.com/p/2b90aa96c0a0)

前言

一個(gè)已經(jīng)發(fā)布到AppStore上的App噩咪,最忌諱的就是崩潰問題顾彰。為什么在開發(fā)階段或者測(cè)試階段都不會(huì)崩潰,而發(fā)布到AppStore上就崩潰了呢胃碾?究其根源涨享,最主要的原因就是數(shù)據(jù)的錯(cuò)亂。特別是 服務(wù)器返回?cái)?shù)據(jù)的錯(cuò)亂仆百,將嚴(yán)重影響到我們的App厕隧。

Foundation框架存在許多潛在崩潰的危險(xiǎn)

將 nil 插入可變數(shù)組中會(huì)導(dǎo)致崩潰。

數(shù)組越界會(huì)導(dǎo)致崩潰俄周。

根據(jù)key給字典某個(gè)元素重新賦值時(shí)吁讨,若key為 nil 會(huì)導(dǎo)致崩潰。

-......

---

AvoidCrash簡(jiǎn)介

===

-這個(gè)框架利用runtime技術(shù)對(duì)一些常用并且容易導(dǎo)致崩潰的方法進(jìn)行處理峦朗,可以有效的防止崩潰建丧。

-并且打印出具體是哪個(gè)方法會(huì)導(dǎo)致崩潰,讓你快速定位導(dǎo)致崩潰的代碼波势。

-你可以獲取到原本導(dǎo)致崩潰的主要信息<由于這個(gè)框架的存在翎朱,并不會(huì)崩潰>,進(jìn)行相應(yīng)的處理尺铣。比如:

? -你可以將這些崩潰信息發(fā)送到自己服務(wù)器拴曲。

? -你若集成了第三方崩潰日志收集的SDK,比如你用了騰訊的Bugly,你可以上報(bào)自定義異常。

-或許你會(huì)問就算防止了崩潰迄埃,但是所獲取到的數(shù)據(jù)變成nil或者并非是你所需要的數(shù)據(jù)疗韵,這又有什么用?對(duì)于防止崩潰侄非,我的理解是蕉汪,寧愿一個(gè)功能不能用,都要讓app活著逞怨,至少其他功能還能用者疤。

下面先來看下防止崩潰的效果吧

`可導(dǎo)致崩潰的代碼`

```

? ? NSString *nilStr = nil;

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

```

-若沒有AvoidCrash來防止崩潰,則會(huì)直接崩潰叠赦,如下圖

![崩潰截圖.png](https://raw.githubusercontent.com/chenfanfang/AvoidCrash/66b631627443490776f964d5f6cdc0d9215d7b09/AvoidCrashDemo/Screenshot/%E5%B4%A9%E6%BA%83%E6%88%AA%E5%9B%BE.png)

-若有AvoidCrash來防止崩潰驹马,則不會(huì)崩潰,并且會(huì)將原本會(huì)崩潰情況的詳細(xì)信息打印出來除秀,如下圖

![防止崩潰輸出日志.png](https://raw.githubusercontent.com/chenfanfang/AvoidCrash/66b631627443490776f964d5f6cdc0d9215d7b09/AvoidCrashDemo/Screenshot/%E9%98%B2%E6%AD%A2%E5%B4%A9%E6%BA%83%E7%9A%84%E8%BE%93%E5%87%BA%E6%97%A5%E5%BF%97.png)

---

## Installation【安裝】

### From CocoaPods【使用CocoaPods】

```ruby

pod 'AvoidCrash', '~> 2.3.0-beta'

```

### Manually【手動(dòng)導(dǎo)入】

-Drag all source files under floder`AvoidCrash`to your project.【將`AvoidCrash`文件夾中的所有源代碼拽入項(xiàng)目中】

-對(duì) NSMutableArray+AvoidCrash.m 文件進(jìn)行 -fno-objc-arc 設(shè)置(若使用CocoaPods集成則無需手動(dòng)配置)糯累,配置過程如下圖:

![](https://raw.githubusercontent.com/chenfanfang/AvoidCrash/e955af927c5ed57f783a71eaca19cb3f028377d0/AvoidCrashDemo/Screenshot/%E9%85%8D%E7%BD%AEmutableArray.png)

---

使用方法

===

-AvoidCrash使用注意點(diǎn)講解

```

?? ? ? //讓AvoidCrash生效方法有兩個(gè)becomeEffective和makeAllEffective,若都不調(diào)用册踩,則AvoidCrash就不起作用

?? ? ? [AvoidCrash becomeEffective]; //【默認(rèn)不開啟? 對(duì)”unrecognized selector sent to instance”防止崩潰的處理】


?? ? ? //若要開啟對(duì)對(duì)”unrecognized selector sent to instance”防止崩潰的處理】泳姐,請(qǐng)使用

?? ? ? //[AvoidCrash makeAllEffective],使用注意點(diǎn),請(qǐng)看AvoidCrash.h中的描述暂吉,必須配合[AvoidCrash setupNoneSelClassStringsArr:]的使用

?? ? ? //【建議在didFinishLaunchingWithOptions最初始位置調(diào)用】[AvoidCrash makeAllEffective]


?? ? /*

? ? ? [AvoidCrash becomeEffective]和[AvoidCrash makeAllEffective]是全局生效胖秒。若你只需要部分生效缎患,你可以單個(gè)進(jìn)行處理,比如:

? ? ? [NSArray avoidCrashExchangeMethod];

? ? ? [NSMutableArray avoidCrashExchangeMethod];

? ? ? .................

? ? ? .................

? ? ? */

```

-在AppDelegate的didFinishLaunchingWithOptions方法中的最初始位置添加如下代碼阎肝,讓AvoidCrash生效

```

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {


? ? //啟動(dòng)防止崩潰功能(注意區(qū)分becomeEffective和makeAllEffective的區(qū)別)

? ? //具體區(qū)別請(qǐng)看 AvoidCrash.h中的描述

? ? //建議在didFinishLaunchingWithOptions最初始位置調(diào)用 上面的方法


? ? [AvoidCrash makeAllEffective];


? ? //若出現(xiàn)unrecognized selector sent to instance導(dǎo)致的崩潰并且控制臺(tái)輸出:

? ? //-[__NSCFConstantString initWithName:age:height:weight:]: unrecognized selector sent to instance

? ? //你可以將@"__NSCFConstantString"添加到如下數(shù)組中挤渔,當(dāng)然,你也可以將它的父類添加到下面數(shù)組中

? ? //比如风题,對(duì)于部分字符串判导,繼承關(guān)系如下

? ? //__NSCFConstantString --> __NSCFString --> NSMutableString --> NSString

? ? //你可以將上面四個(gè)類隨意一個(gè)添加到下面的數(shù)組中锚扎,建議直接填入 NSString

? ? NSArray *noneSelClassStrings = @[

?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? @"NSString"

?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ];

? ? [AvoidCrash setupNoneSelClassStringsArr:noneSelClassStrings];



? ? //監(jiān)聽通知:AvoidCrashNotification, 獲取AvoidCrash捕獲的崩潰日志的詳細(xì)信息

? ? [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(dealwithCrashMessage:) name:AvoidCrashNotification object:nil];

? ? return YES;

}

```

-若你想要獲取崩潰日志的所有詳細(xì)信息隔盛,只需添加通知的監(jiān)聽,監(jiān)聽的通知名為:AvoidCrashNotification

```

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {


? ? [AvoidCrash becomeEffective];


? ? //監(jiān)聽通知:AvoidCrashNotification, 獲取AvoidCrash捕獲的崩潰日志的詳細(xì)信息

? ? [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(dealwithCrashMessage:) name:AvoidCrashNotification object:nil];

? ? return YES;

}

- (void)dealwithCrashMessage:(NSNotification *)note {

? ? //注意:所有的信息都在userInfo中

? ? //你可以在這里收集相應(yīng)的崩潰信息進(jìn)行相應(yīng)的處理(比如傳到自己服務(wù)器)

? ? NSLog(@"%@",note.userInfo);

}

```

-下面通過打斷點(diǎn)的形式來看下userInfo中的信息結(jié)構(gòu)赴肚,看下包含了哪些信息

![userInfo信息結(jié)構(gòu).png](https://raw.githubusercontent.com/chenfanfang/AvoidCrash/66b631627443490776f964d5f6cdc0d9215d7b09/AvoidCrashDemo/Screenshot/userInfo%E4%BF%A1%E6%81%AF%E7%BB%93%E6%9E%84.png)

-再看下控制臺(tái)輸出日志來看下userInfo中的包含了哪些信息

![userInfo詳細(xì)信息](https://raw.githubusercontent.com/chenfanfang/AvoidCrash/556cab1b9fa25c8265dd1e8a19c816db20e93c24/AvoidCrashDemo/Screenshot/userInfo%E8%AF%A6%E7%BB%86%E4%BF%A1%E6%81%AF.png)

---

目前可以防止崩潰的方法有

===

---

-unrecognized selector sent to instance

?? -? `1. 對(duì)”unrecognized selector sent to instance”防止崩潰的處理`

---

?-NSArray

?? -? `1. NSArray的快速創(chuàng)建方式 NSArray *array = @[@"chenfanfang", @"AvoidCrash"];? //這種創(chuàng)建方式其實(shí)調(diào)用的是2中的方法`

?? -? `2. +(instancetype)arrayWithObjects:(const id? _Nonnull __unsafe_unretained *)objects count:(NSUInteger)cnt`


?? - `3. 通過下標(biāo)獲取元素 array[100]稽鞭、[array objectAtIndex:100]`

?? ? - `- (id)objectAtIndex:(NSUInteger)index`


?? - `4. - (NSArray *)objectsAtIndexes:(NSIndexSet *)indexes`

?? - `5. - (void)getObjects:(__unsafe_unretained id? _Nonnull *)objects range:(NSRange)range`


---

-NSMutableArray?

? - `1. 通過下標(biāo)獲取元素 array[100]鸟整、[array objectAtIndex:100]`

? ? ? - `- (id)objectAtIndex:(NSUInteger)index`

? - `2. - (void)setObject:(id)obj atIndexedSubscript:(NSUInteger)idx`

? - `3. - (void)removeObjectAtIndex:(NSUInteger)index`

? - `4. - (void)insertObject:(id)anObject atIndex:(NSUInteger)index`

? - `5. - (NSArray *)objectsAtIndexes:(NSIndexSet *)indexes`

? - `6. - (void)getObjects:(__unsafe_unretained id? _Nonnull *)objects range:(NSRange)range`


---

-NSDictionary

? - `1. NSDictionary的快速創(chuàng)建方式 NSDictionary *dict = @{@"frameWork" : @"AvoidCrash"}; //這種創(chuàng)建方式其實(shí)調(diào)用的是2中的方法`

? - `2. +(instancetype)dictionaryWithObjects:(const id? _Nonnull __unsafe_unretained *)objects forKeys:(const id? _Nonnull __unsafe_unretained *)keys count:(NSUInteger)cnt`

---

-NSMutableDictionary

? - `1. - (void)setObject:(id)anObject forKey:(id)aKey`

? - `2. - (void)removeObjectForKey:(id)aKey`




---

-NSString

- `1. - (unichar)characterAtIndex:(NSUInteger)index`

- `2. - (NSString *)substringFromIndex:(NSUInteger)from`

- `3. - (NSString *)substringToIndex:(NSUInteger)to {`

- `4. - (NSString *)substringWithRange:(NSRange)range {`

- `5. - (NSString *)stringByReplacingOccurrencesOfString:(NSString *)target withString:(NSString *)replacement`

- `6. - (NSString *)stringByReplacingOccurrencesOfString:(NSString *)target withString:(NSString *)replacement options:(NSStringCompareOptions)options range:(NSRange)searchRange`

- `7. - (NSString *)stringByReplacingCharactersInRange:(NSRange)range withString:(NSString *)replacement`

---

-NSMutableString

- `1. 由于NSMutableString是繼承于NSString,所以這里和NSString有些同樣的方法就不重復(fù)寫了`

- `2. - (void)replaceCharactersInRange:(NSRange)range withString:(NSString *)aString`

- `3. - (void)insertString:(NSString *)aString atIndex:(NSUInteger)loc`

- `4. - (void)deleteCharactersInRange:(NSRange)range`


---

-KVC

? -? `1.- (void)setValue:(id)value forKey:(NSString *)key`

? -? `2.- (void)setValue:(id)value forKeyPath:(NSString *)keyPath`

? -? `3.- (void)setValue:(id)value forUndefinedKey:(NSString *)key //這個(gè)方法一般用來重寫,不會(huì)主動(dòng)調(diào)用`

? -? `4.- (void)setValuesForKeysWithDictionary:(NSDictionary *)keyedValues`

---

-NSAttributedString

? -? `1.- (instancetype)initWithString:(NSString *)str`

? -? `2.- (instancetype)initWithAttributedString:(NSAttributedString *)attrStr`

? -? `3.- (instancetype)initWithString:(NSString *)str attributes:(NSDictionary *)attrs`

---

-NSMutableAttributedString

? -? `1.- (instancetype)initWithString:(NSString *)str`

? -? `2.- (instancetype)initWithString:(NSString *)str attributes:(NSDictionary *)attrs`

更新

===

#### 2017-12-26 (pod 2.5.1)

- 兼容iOS11的處理

#### 2017-08-11

- 優(yōu)化對(duì)”unrecognized selector sent to instance”防止崩潰的處理

#### 2017-07-25

- 優(yōu)化對(duì)”unrecognized selector sent to instance”防止崩潰的處理

#### 2017-07-23

- 增加對(duì)”unrecognized selector sent to instance”防止崩潰的處理

---

#### 2016-12-19

-Release環(huán)境下取消控制臺(tái)的輸出朦蕴。

---

#### 2016-12-1

-處理數(shù)組的類簇問題篮条,提高兼容性,不論是由于array[100]方式吩抓,還是[array objectAtIndex:100]方式 獲取數(shù)組中的某個(gè)元素操作不當(dāng)而導(dǎo)致的crash,都能被攔截防止崩潰涉茧。

?-上一個(gè)版本只能防止array[100]導(dǎo)致的崩潰,不能防止[array objectAtIndex:100]導(dǎo)致的崩潰疹娶。

-統(tǒng)一對(duì)線程進(jìn)行處理伴栓,監(jiān)聽通知AvoidCrashNotification后,不論是在主線程導(dǎo)致的crash還是在子線程導(dǎo)致的crash雨饺,監(jiān)聽通知的方法統(tǒng)一在"主線程"中钳垮。

?-上一個(gè)版本中,在哪個(gè)線程導(dǎo)致的crash, 則監(jiān)聽通知的方法就在哪個(gè)線程中额港。

- 新增防止崩潰 (NSArray饺窿、NSMutableArray) `- (void)getObjects:(__unsafe_unretained id? _Nonnull *)objects range:(NSRange)range`

---

#### 2016-11-29

-修復(fù)在鍵盤彈出狀態(tài)下,按Home鍵進(jìn)入后臺(tái)會(huì)導(dǎo)致崩潰的bug移斩。

- 新增防止崩潰(NSArray肚医、NSMutableArray) `- (NSArray *)objectsAtIndexes:(NSIndexSet *)indexes`

---

#### 2016-10-15

-修復(fù)上一個(gè)版本部分方法不能攔截崩潰的BUG,具體修復(fù)哪些可以查看issues和簡(jiǎn)書上的留言向瓷。

-優(yōu)化崩潰代碼的定位肠套,定位崩潰代碼更加準(zhǔn)確。

-增加對(duì)KVC賦值防止崩潰的處理猖任。

-增加對(duì)NSAttributedString防止崩潰的處理

-增加對(duì)NSMutableAttributedString防止崩潰的處理

---

提示

===

-1你稚、由于@try @catch的原因,如果防止了你項(xiàng)目中將要crash的代碼,有些方法將導(dǎo)致些許的內(nèi)存泄漏入宦。若你的代碼不會(huì)導(dǎo)致crash,當(dāng)然就不存在內(nèi)存泄漏的問題,crash和些許內(nèi)存泄漏的選擇當(dāng)然取決于你自己室琢。目前發(fā)現(xiàn)的內(nèi)存泄漏的方法如下圖所示乾闰,沒有顯示在下圖中的方法,不論是否會(huì)導(dǎo)致crash盈滴,都不會(huì)有內(nèi)存泄漏涯肩。


? ![](https://raw.githubusercontent.com/chenfanfang/AvoidCrash/fdad9c8808559c0b20c8672b2cb6e901d4e4f006/AvoidCrashDemo/Screenshot/Leaks.png)


-2、有朋友提出巢钓,AvoidCraah和[RegexKitLite](https://github.com/wezm/RegexKitLite)有沖突病苗,畢竟代碼不在同一個(gè)時(shí)代上的(RegexKitLite最后一次提交時(shí)在2011年)。同時(shí)也說明AvoidCrash的健壯性不夠症汹,大家若有什么意見可以提出硫朦。

期待

===

*如果你發(fā)現(xiàn)了哪些常用的Foundation中的方法存在潛在崩潰的危險(xiǎn),而這個(gè)框架中沒有進(jìn)行處理背镇,希望你能 issue, 或者在簡(jiǎn)書私信我,我將這些方法也添加到AvoidCrash中咬展。謝謝

*如果你在使用過程中遇到BUG,希望你能 issue, 或者在簡(jiǎn)書私信我瞒斩。謝謝(或者嘗試下載最新的框架代碼看看BUG修復(fù)沒有)

*畢竟一個(gè)人的能力有限破婆,時(shí)間有限,希望有能力的你可以加入到這個(gè)項(xiàng)目中來胸囱,一起完善AvoidCrash祷舀。請(qǐng)Pull Requests我。

來源于:http://www.reibang.com/users/80fadb71940d/latest_articles

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末烹笔,一起剝皮案震驚了整個(gè)濱河市裳扯,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌谤职,老刑警劉巖嚎朽,帶你破解...
    沈念sama閱讀 219,270評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異柬帕,居然都是意外死亡哟忍,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門陷寝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來锅很,“玉大人,你說我怎么就攤上這事凤跑”玻” “怎么了?”我有些...
    開封第一講書人閱讀 165,630評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵仔引,是天一觀的道長(zhǎng)扔仓。 經(jīng)常有香客問我褐奥,道長(zhǎng),這世上最難降的妖魔是什么翘簇? 我笑而不...
    開封第一講書人閱讀 58,906評(píng)論 1 295
  • 正文 為了忘掉前任撬码,我火速辦了婚禮,結(jié)果婚禮上版保,老公的妹妹穿的比我還像新娘呜笑。我一直安慰自己,他們只是感情好彻犁,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,928評(píng)論 6 392
  • 文/花漫 我一把揭開白布叫胁。 她就那樣靜靜地躺著,像睡著了一般汞幢。 火紅的嫁衣襯著肌膚如雪驼鹅。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,718評(píng)論 1 305
  • 那天森篷,我揣著相機(jī)與錄音谤民,去河邊找鬼。 笑死疾宏,一個(gè)胖子當(dāng)著我的面吹牛张足,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播坎藐,決...
    沈念sama閱讀 40,442評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼为牍,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了岩馍?” 一聲冷哼從身側(cè)響起碉咆,我...
    開封第一講書人閱讀 39,345評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蛀恩,沒想到半個(gè)月后疫铜,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,802評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡双谆,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,984評(píng)論 3 337
  • 正文 我和宋清朗相戀三年壳咕,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片顽馋。...
    茶點(diǎn)故事閱讀 40,117評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡谓厘,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出寸谜,到底是詐尸還是另有隱情竟稳,我是刑警寧澤,帶...
    沈念sama閱讀 35,810評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站他爸,受9級(jí)特大地震影響聂宾,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜诊笤,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,462評(píng)論 3 331
  • 文/蒙蒙 一系谐、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧盏混,春花似錦、人聲如沸惜论。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽馆类。三九已至混聊,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間乾巧,已是汗流浹背句喜。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留沟于,地道東北人咳胃。 一個(gè)月前我還...
    沈念sama閱讀 48,377評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像旷太,于是被迫代替她去往敵國(guó)和親展懈。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,060評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容