利用Runtime解决unrecognized selector经典错误

·  阅读 318

在日常开发中我们经常会遇到unrecognized selector这个令人头疼的问题。往往程序崩溃不说,还不容易定位到错误的位位置,那么我们今天通过runtime黑魔法来解决这个问题。

运行截图:我们在Person_UnRecognized类中声明test方法,但是没有对test方法进行实现。

image.png

image.png

//
//  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
复制代码
分类:
iOS
标签:
分类:
iOS
标签:
收藏成功!
已添加到「」, 点击更改