在日常开发中我们经常会遇到unrecognized selector这个令人头疼的问题。往往程序崩溃不说,还不容易定位到错误的位位置,那么我们今天通过runtime黑魔法来解决这个问题。
运行截图:我们在Person_UnRecognized类中声明test方法,但是没有对test方法进行实现。
//
// NSObject+UnRecognized.h
// ObjectiveCDemo
//
// Created by XiaoBai on 2022/3/28.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface NSObject (UnRecognized)
@end
NS_ASSUME_NONNULL_END
复制代码
//NSObject+UnRecognized.m
//
// NSObject+UnRecognized.m
// ObjectiveCDemo
//
// Created by XiaoBai on 2022/3/28.
//
#import "NSObject+UnRecognized.h"
#import <objc/runtime.h>
@implementation NSObject (UnRecognized)
+(void)load {
//保证load方法只加载一次
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
YH_swizzledMethod(self,@selector(methodSignatureForSelector:),@selector(YH_methodSignatureForSelector:));
YH_swizzledMethod(self,@selector(forwardInvocation:),@selector(YH_forwardInvocation:));
});
}
-(NSMethodSignature *)YH_methodSignatureForSelector:(SEL)aSelector {
//如果以前的方法本来就能处理,那么就调用系统原来的方法
if([self respondsToSelector:aSelector]) {
return [self YH_methodSignatureForSelector:aSelector];
}
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
-(void)YH_forwardInvocation:(NSInvocation *)anInvocation {
//可以做上传服务器等操作等
NSLog(@"找不到对象:%@的方法:%@",anInvocation.target,NSStringFromSelector(anInvocation.selector));
}
/// 方法交换
/// @param cls 要交换的类对象
/// @param originSelector 原始方法
/// @param swizzledSelector 交换后的方法
void YH_swizzledMethod(Class cls,
SEL originSelector,
SEL swizzledSelector) {
Method swizzledMethod = class_getInstanceMethod(cls, swizzledSelector);
/**
* class_addMethod(Class _Nullable cls,
* SEL _Nonnull name,
* IMP _Nonnull imp,
const char * _Nullable types)
* 向指定的cls的指定方法name添加一个方法的实现
*
*/
BOOL res = class_addMethod(cls, originSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
//如果添加成功的话。直接替换原来方法的实现。
if(res) {
class_replaceMethod(cls, originSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
}else {
//如果添加失败(可能由于原来方法的实现已经存在的话)。那么直接交换两个方法的实现就行了
Method originMethod = class_getInstanceMethod(cls, originSelector);
method_exchangeImplementations(originMethod, swizzledMethod);
}
}
@end
复制代码