首先參考一下自己之前寫的《method swizzing》這篇礁叔,特別是對(duì)類簇的methodSwizzing。
Container 類型的crash 指的是容器類的crash俏让,常見的有NSArray/NSMutableArray/NSDictionary/NSMutableDictionary/NSCache的crash。 一些常見的越界、插入nil等錯(cuò)誤操作均會(huì)導(dǎo)致此類crash發(fā)生乾吻。 由于產(chǎn)生的原因比較簡(jiǎn)單,就不展開來(lái)描述了拟蜻。
該類crash雖然比較容易排查绎签,但是其在app crash概率總比還是挺高,所以有必要對(duì)其進(jìn)行防護(hù)酝锅。另外NSString也有類似的crash防護(hù)措施诡必,在此篇中一并舉例說(shuō)明。
一搔扁、Container類型和NSString類型常見crash
(1) [aMutableDictionary setObject:nil forKey:]; object can not be nil.
(2) [aString hasSuffix:nil]; nil argument crash.
[aString hasPrefix:nil]; nil argument crash.
(3) aString = [NSMutableString stringWithString:nil];nil argument crash.
(4) aString = [[NSString alloc] initWithString:nil]; nil argument crash.
(5) aURL = [NSURL fileURLWithPath:nil]; nil argument crash.
(6) NSArray 數(shù)組越界 crash爸舒。
二、使用method swizzing對(duì)常見crash防護(hù)舉例
這里對(duì)NSString稿蹲、NSMutableDictionary和NSArray常見的幾個(gè)crash舉例如下:
(1)對(duì)NSString的hasSuffix:和hasPrefix:進(jìn)行預(yù)防:
#import "NSString+CrashGurad.h"
#import <objc/runtime.h>
@implementation NSString (CrashGurad)
#pragma mark Class Method
+(void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[[self class] swizzedMethod:@selector(hasSuffix:) withMethod:@selector(crashGuard_hasSuffix:)];
[[self class] swizzedMethod:@selector(hasPrefix:) withMethod:@selector(crashGuard_hasPrefix:)];
});
}
+(void)swizzedMethod:(SEL)originalSelector withMethod:(SEL )swizzledSelector {
Method fromMethod = class_getInstanceMethod(objc_getClass("__NSCFConstantString"), originalSelector);
Method toMethod = class_getInstanceMethod(objc_getClass("__NSCFConstantString"), swizzledSelector);
method_exchangeImplementations(fromMethod, toMethod);
}
#pragma mark Swizzled Method
-(BOOL)crashGuard_hasSuffix:(NSString *)str {
if(!str){
// 打印崩潰信息扭勉,棧信息 等
NSLog(@"selector \"hasSuffix\" crash for the the suffix is nil!");
return NO;
} else {
return [self crashGuard_hasSuffix:str];
}
}
- (BOOL)crashGuard_hasPrefix:(NSString *)str {
if(!str){
// 打印崩潰信息,棧信息 等
NSLog(@"selector \"hasPrefix\" crash for the the prefix is nil!");
return NO;
} else {
return [self crashGuard_hasPrefix:str];
}
}
(2)對(duì)NSArray的objectAtIndex:的crash預(yù)防如下:
#import "NSArray+CrashGuard.h"
#import <objc/runtime.h>
@implementation NSArray (CrashGuard)
+(void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Method fromMethod = class_getInstanceMethod(objc_getClass("__NSArrayI"), @selector(objectAtIndex:));
Method toMethod = class_getInstanceMethod(objc_getClass("__NSArrayI"), @selector(crashGuard_objectAtIndex:));
method_exchangeImplementations(fromMethod, toMethod);
});
}
#pragma mark Swizzled Method
-(id)crashGuard_objectAtIndex:(NSUInteger)index {
if(self.count-1 < index) {
// 打印崩潰信息苛聘,棧信息 等
NSLog(@"selector \"objectAtIndex\" crash for the index beyond the boundary!");
return nil;
} else {
return [self crashGuard_objectAtIndex:index];
}
}
@end
3)對(duì)NSMutableDictionary的setObject:forKey:的crash預(yù)防如下:
#import "NSMutableDictionary+CrashGuard.h"
#import <objc/runtime.h>
@implementation NSMutableDictionary (CrashGuard)
+(void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Method fromMethod = class_getInstanceMethod(objc_getClass("__NSDictionaryM"), @selector(setObject:forKey:));
Method toMethod = class_getInstanceMethod(objc_getClass("__NSDictionaryM"), @selector(crashGuard_setObject:forKey:));
method_exchangeImplementations(fromMethod, toMethod);
});
}
#pragma mark Swizzled Method
-(void)crashGuard_setObject:(id)object forKey:(NSString *)key {
if(!object) {
// 打印崩潰信息涂炎,棧信息 等
NSLog(@"selector \"setObject:forKey:\" crash for the the object is nil!");
} else {
[self crashGuard_setObject:object forKey:key];
}
}
@end
總結(jié):
Container crash 類型和NSString類型的crash防護(hù)方案比較簡(jiǎn)單忠聚,針對(duì)于NSArray/NSMutableArray/NSDictionary/NSMutableDictionary的一些常用的會(huì)導(dǎo)致崩潰的API進(jìn)行method swizzling,然后在swizzle的新方法中加入一些條件限制和判斷唱捣,從而讓這些API變的安全两蟀。當(dāng)然,在程序進(jìn)入這些swizzle的方法后爷光,我們需要及時(shí)記錄并上傳相關(guān)的日志信息到后臺(tái)服務(wù)器垫竞,并及時(shí)review相關(guān)的內(nèi)容,將迭代中將有隱患的代碼改掉蛀序。