AFNetworking開(kāi)啟removesKeysWithNullValues = YES
- 好處:可自動(dòng)過(guò)濾后臺(tái)接口返回的null
-
原因:后臺(tái)返回的null會(huì)通過(guò)NSNull接收,一旦對(duì)NSNull發(fā)消息會(huì)造成閃退
image.png
使用YYModel接收后臺(tái)接口返回的data
- 好處:YYModel在Json轉(zhuǎn)Model時(shí),會(huì)自動(dòng)把值為null的字段轉(zhuǎn)為nil,而對(duì)nil發(fā)消息不會(huì)閃退
- 其他:如果后臺(tái)返回的data嵌套了多層,建議通過(guò)多層YYModel的方法解析Data,避免取到空值。例如,后臺(tái)返回的json中包含兩層Dict,應(yīng)創(chuàng)建兩個(gè)Model分別接收。
- 推薦寫(xiě)法:
@interface HR_Applicant : MP_BaseModel
@property (nonatomic,copy ) NSString *applicantId;
@property (nonatomic,copy ) NSString *name;
@property (nonatomic,strong) HR_Resume *resume;
@end
@interface HR_Resume : MP_BaseModel
@property (nonatomic,copy ) NSString *birthYear;
@property (nonatomic,copy ) NSString *imageUrls;
@end
- 不推薦寫(xiě)法:
@interface HR_Applicant : MP_BaseModel
@property (nonatomic,copy ) NSString *applicantId;
@property (nonatomic,copy ) NSString *name;
@property (nonatomic,strong) NSDictionary *resume;
@end
@implementation HR_Applicant
- (void)parseResumeData{
NSDictionary *responseDict = [self responseObject];
NSDictionary *resume = responseDict[@"resume"];
self.resume = resume;
}
@end
通過(guò)宏定義的寫(xiě)法來(lái)判空
原因:宏定義中進(jìn)行了“類(lèi)型判斷”、“null判斷”、“空值判斷”,判空會(huì)更加嚴(yán)謹(jǐn)
- 推薦寫(xiě)法:
- (void)demo{
NSString *str = [self getString];
if (kStringIsEmpty(str)) {
return;
}
NSArray *arr = [self getArray];
if (kArrayIsEmpty(arr)) {
return;
}
NSDictionary *dict = [self getDict];
if (kDictIsEmpty(dict)) {
return;
}
//......
}
- 不推薦寫(xiě)法:
- (void)demo{
NSString *str = [self getString];
if (str.length == 0) {
return;
}
NSArray *arr = [self getArray];
if (arr.count == 0) {
return;
}
NSDictionary *dict = [self getDict];
if (dict.allKeys.count == 0) {
return;
}
// ......
}
通過(guò)JKCategories對(duì)NSDictionary進(jìn)行取值和賦值
- 原因:JKCategories內(nèi)部進(jìn)行了判空和類(lèi)型轉(zhuǎn)換,并采用KVC來(lái)取值和賦值,可以避免閃退
- 推薦寫(xiě)法:
//賦值
NSString *str = [self getString];
BOOL boolValue = [self getBool];
NSMutableDictionary *dictM = [NSMutableDictionary dictionary];
[dictM jk_setString:str forKey:@"str"];
[dictM jk_setBool:boolValue forKey:@"bool"];
//取值
NSString *str = [dict jk_stringForKey:@"str"];
- 不推薦寫(xiě)法:
//賦值
NSString *str = [self getString];
BOOL boolValue = [self getBool];
NSDictionary *dict = @{@"str" : str,
@"bool" : @(boolValue)};
//取值
NSString *str = dict[@"str"];
開(kāi)啟JJException,自動(dòng)攔截閃退。
- 具體原理可參考:https://github.com/jezzmemo/JJException
- JJException已Hook掉的系統(tǒng)API可參考:https://github.com/jezzmemo/JJException/blob/master/JJExceptionHookAPI.md
- 通過(guò)Hook掉系統(tǒng)的API,JJException目前可攔截以下閃退類(lèi)型:
- Unrecognized Selector Sent to Instance(方法不存在異常)
- NSNull(方法不存在異常,本質(zhì)上跟Unrecognized Selector一樣)
- NSString,NSMutableString,NSAttributedString,NSMutableAttributedString(下標(biāo)越界以及參數(shù)nil異常)
- NSArray,NSMutableArray,NSDictonary,NSMutableDictionary(數(shù)組越界,key-value參數(shù)異常)
- KVO(忘記移除keypath導(dǎo)致閃退)
- Zombie Pointer(野指針)
- NSTimer(忘記移除導(dǎo)致內(nèi)存泄漏)
- NSNotification(忘記移除導(dǎo)致異常)
通過(guò)MethodSwizzle技術(shù),hook掉系統(tǒng)方法,攔截閃退
- 原因:對(duì)于JJException無(wú)法攔截的閃退,我們可以通過(guò)自己Hook相關(guān)方法來(lái)實(shí)現(xiàn)閃退攔截。
- 舉例:
閃退原因:attempt to delete row 1 from section 0 which only contains 1 rows before the update
上述閃退發(fā)生的原因:tableView通過(guò)系統(tǒng)方法reloadRowsAtIndexPaths:withRowAnimation:試圖刷新一個(gè)越界的NSIndexPath。
解決方法:hook掉reloadRowsAtIndexPaths:withRowAnimation:方法,先做判斷,再執(zhí)行。
代碼示例:
@implementation UITableView (FixCrash)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Method originalMethod = class_getInstanceMethod(self.class, @selector(reloadRowsAtIndexPaths:withRowAnimation:));
Method swizzleMethod = class_getInstanceMethod(self.class, @selector(safeReloadRowsAtIndexPaths:withRowAnimation:));
method_exchangeImplementations(originalMethod, swizzleMethod);
});
}
- (void)safeReloadRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation{
NSUInteger totalSectionCount = self.numberOfSections;
for (NSIndexPath *indexPath in indexPaths) {
if (indexPath.section >= totalSectionCount) {
return;
}
NSUInteger totalRowCount = [self numberOfRowsInSection:indexPath.section];
if (indexPath.row >= totalRowCount) {
return;
}
}
[self safeReloadRowsAtIndexPaths:indexPaths withRowAnimation:animation];
}
@end
第三方閃退(例如高德地圖)
- 待解決。。。
