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)
动态添加的方法:
方法替换:
如果到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
我的希望就是尽量对源代码少的改动,增加程序的健壮性,避免由于异常情况导致的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
相关文章推荐
- 如何在 iOS 上避免 SIGPIPE 信号导致的 crash (Avoiding SIGPIPE signal crash in iOS)
- Xamarin.iOS binding项目如何链接动态库
- 如何优雅的上传iOS项目到应用商店
- 如何架构一个ios项目 个人经验总结
- 如何给你iOS项目选择最合适的XML解析方式
- 【iOS开发-94】xcode6怎么使用GIT以及如何给老得项目增加GIT功能?
- 一个iOS项目中包含多个xcodeproj文件,如何运行其中的一个项目
- 如何更改iOS项目名称
- 【iOS Crash文件分析】-如何使用symbolicatecrash工具
- iOS 如何把项目托管到GitHub
- iOS开发:开发中如何更改项目名
- iOS如何彻底避免数组越界
- 如何搭建iOS项目基本框架
- 如何给你iOS项目选择最合适的XML解析方式
- 如何修改iOS APP的项目名称
- iOS开发拓展篇——如何把项目托管到GitHub
- 如何查看iOS已上架app崩溃分布、定位crash发生的行
- iOS开发 -- Xcode7如何创建项目启动图!?
- 【腾讯bugly干货分享】开发iOS应用如何避免卡顿
- IOS调试的时候无dSYM 出现crash如何分析