疑惑解答
很多開發(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ì)直接崩潰叠赦,如下圖

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

---
## 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)配置)糯累,配置過程如下圖:

---
使用方法
===
-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)赴肚,看下包含了哪些信息

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

---
目前可以防止崩潰的方法有
===
---
-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)存泄漏涯肩。
? 
-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