您的位置:首页 > 移动开发 > IOS开发

ios项目如何避免crash

2017-12-01 11:19 267 查看
随着app不断的迭代,代码会变得越来越多,经过N个人的持续N年的代码,维护起来越来越难,也很难保证测试case覆盖所有场景。举一个例子:如果原来服务器返回的是数组,而现在返回字符串,如果代码上没有添加判断,很可能就会造成“unrecognized selector sent to instance”,谁也不知道当初这么写的逻辑,没人敢动老代码。几万甚至数十万行的代码,这样的风险不可避免。

我的希望就是尽量对源代码少的改动,增加程序的健壮性,避免由于异常情况导致的crash。目前主要用到的原理就是通过方法替换和消息转发的机制,避免因为程序缺少判断而导致crash。

消息转发大家并不陌生,如果遇到unrecognized selector sent to instance,我可以通过消息转发捕捉的这个情况,而且因为很多框架已经使用了这个机制,所以尽量避免与其它框架的冲突(如JSPatch)

- (NSMethodSignature *)swizz_instance_methodSignatureForSelector:(SEL)aSelector {

//默认先调用原始方法,判断用户有没有对此进行处理,如果没有就会unrecognized异常,这里就要加上处理
NSMethodSignature *sig = [self swizz_instance_methodSignatureForSelector:aSelector];
if (sig == nil) {
if (![[LongCrashManager sharedInstancel] respondsToSelector:aSelector]) {
//动态的给一个实例添加一个方法,去处理这个unrecognized selector
class_addMethod([LongCrashManager class], aSelector, (IMP)dynamicMethodIMP, "v@:");
}
sig = [[LongCrashManager sharedInstancel] methodSignatureForSelector:aSelector];
[[LongCrashManager sharedInstancel] onCrashWithInfo:[NSString stringWithFormat:@"LongCrash|[%@ %@]:unrecognized selector sent to class %p|instance" ,[self class] ,NSStringFromSelector(aSelector) ,self]];
}
return sig;
}

- (void)swizz_instance_forwardInvocation:(NSInvocation *)aInvocation
{
id target = nil;
if ([[LongCrashManager sharedInstancel] methodSignatureForSelector:[aInvocation selector]] ) {
target = [LongCrashManager sharedInstancel];
[aInvocation invokeWithTarget:target];
} else {
[self swizz_instance_forwardInvocation:aInvocation];
}
}


动态添加的方法:

void dynamicMethodIMP(id self, SEL _cmd) {
//do nothing
}


方法替换:

+ (void)long_crash
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
method_exchangeImplementations(class_getInstanceMethod([NSObject class], @selector(forwardInvocation:)), class_getInstanceMethod([self class], @selector(swizz_instance_forwardInvocation:)));

method_exchangeImplementations(class_getInstanceMethod([NSObject class], @selector(methodSignatureForSelector:)), class_getInstanceMethod([self class], @selector(swizz_instance_methodSignatureForSelector:)));

method_exchangeImplementations(class_getClassMethod([NSObject class], @selector(forwardInvocation:)), class_getClassMethod([self class], @selector(swizz_class_forwardInvocation:)));

method_exchangeImplementations(class_getClassMethod([NSObject class], @selector(methodSignatureForSelector:)), class_getClassMethod([self class], @selector(swizz_class_methodSignatureForSelector:)));
});
}


如果到methodSignatureForSelector程序本身还没有处理,这就意味着很有可能导致crash了,为了避免这种情况,我找了一个实例动态添加了这个selector,去接收处理这条消息,通过回调反馈给应用,告诉开发者具体crash的原因。初始化LongCrashManager的时候我会替换NSObject的对应的实例方法和类方法(两个必须都要替换,调用类方法也可能会遇到这种情况),因为OC中所有的类都继承NSObject,所以只要很小的代码改动,就避免了程序因为异常case导致的“unrecognized selector sent to instance”。

至于对性能上的担心,我觉得应该可以忽略,毕竟大部分情况是走不到这里的,而且目前对于正常App来讲,避免Crash更为重要。比如千万日活的APP,因为服务异常导致的数据错误,同时老代码没有针对这一case添加判断,成千上万的crash对于App简直就是致命的

上述提到的代码:https://github.com/lizilong1989/LongCrash
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: