在开发中经常遇到一种错误,就是unrecognized selector sent to instance ***,这种类型的错误,说简单点,就是找不到这个类所对应的方法,这种情况通常是因为开发者的粗心,比如忘记了写按钮的方法实现,或者是服务器数据错误而引起的。
之前有人针对开发中常见的一些崩溃问题做过处理,但是仅限于数组越界,字典key值为空之类的常见错误,比如
这个项目。但是针对未识别方法的崩溃处理,这个项目里是没有的,所以我现在做的这个也算是对他的一种补充吧。
在demo中我们创建了一个红色的btn
[btn addTarget:self action:@selector(click) forControlEvents:UIControlEventTouchUpInside];
但是并没有实现click这个方法,所以再点击按钮后,很自然的崩溃了。如下图
当对崩溃方法进行转发处理后,就不会崩溃了。而是会弹出一个弹框,告诉我们哪个类的什么方法没有找到。在DEBUG模式中,也方便开发者调试。
下面是方法转发的关键代码:
#import "NSObject+UnRecognizedSelHandler.h"
#import <objc/runtime.h>
//提示框--->UIAlertController
#define ALERT_VIEW(Title,Message,Controller) {UIAlertAction *action = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:nil];UIAlertController *alertVc = [UIAlertController alertControllerWithTitle:Title message:Message preferredStyle:UIAlertControllerStyleAlert]; [alertVc addAction:action];[Controller presentViewController:alertVc animated:YES completion:nil];}
#import "AppDelegate.h"
static NSString *_errorFunctionName;
void dynamicMethodIMP(id self,SEL _cmd){
#ifdef DEBUG
AppDelegate *delegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
UIViewController *currentRootViewController = delegate.window.rootViewController;
NSString *error = [NSString stringWithFormat:@"errorClass->:%@\n errorFuction->%@\n errorReason->UnRecognized Selector",NSStringFromClass([self class]),_errorFunctionName];
ALERT_VIEW(@"程序异常",error,currentRootViewController);
#else
//upload error
#endif
}
#pragma mark 方法调换
static inline void change_method(Class _originalClass ,SEL _originalSel,Class _newClass ,SEL _newSel){
Method methodOriginal = class_getInstanceMethod(_originalClass, _originalSel);
Method methodNew = class_getInstanceMethod(_newClass, _newSel);
method_exchangeImplementations(methodOriginal, methodNew);
}
@implementation NSObject (UnRecognizedSelHandler)
+ (void)load{
change_method([self class], @selector(methodSignatureForSelector:), [self class], @selector(SH_methodSignatureForSelector:));
change_method([self class], @selector(forwardInvocation:), [self class], @selector(SH_forwardInvocation:));
}
- (NSMethodSignature *)SH_methodSignatureForSelector:(SEL)aSelector{
if (![self respondsToSelector:aSelector]) {
_errorFunctionName = NSStringFromSelector(aSelector);
NSMethodSignature *methodSignature = [self SH_methodSignatureForSelector:aSelector];
if (class_addMethod([self class], aSelector, (IMP)dynamicMethodIMP, "v@:")) {
NSLog(@"临时方法添加成功!");
}
if (!methodSignature) {
methodSignature = [self SH_methodSignatureForSelector:aSelector];
}
return methodSignature;
}else{
return [self SH_methodSignatureForSelector:aSelector];
}
}
- (void)SH_forwardInvocation:(NSInvocation *)anInvocation{
SEL selector = [anInvocation selector];
if ([self respondsToSelector:selector]) {
[anInvocation invokeWithTarget:self];
}else{
[self SH_forwardInvocation:anInvocation];
}
}
@end
原理就是运用oc的运行时,将系统的转发方法与自己的方法做替换,从而为这个对象重新绑定一个新的方法,新的方法会在当前页面弹出程序的错误信息。也可以在这里将错误的代码信息上传到自己的服务器中。效果如下:
最后可以点击这里下载demo。