解决因为NSNull类型导致出现的unrecognized selector sent to instance问题

问题描述:因为objc是动态语言,对象的类型在运行时才会被确认,所以很容易出现一个定义为NSString类型的变量,在运行时的类型变成了NSNull,从而导致如下错误出现:-[NSNull stringByAppendingFormat:]: unrecognized selector sent to instance

下面介绍一下解决这个问题的思路

首先我们知道objc提供了消息转发机制,可以挽救当一个类调用了不存在的方法时,给你挽救的机会。我们就可以利用这个机制来拯救这个错误。

我们通过创建一个NSNull的分类,然后重写- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector和- (void)forwardInvocation:(NSInvocation *)anInvocation两个方法

1、在methodSignatureForSelector方法体中我们需要遍历系统中所有注册的iOS类,然后找到可以执行aSelector的类,并返回这个类的该aSelector的方法签名对象。

这里涉及以下几点:

    a、遍历所有的已注册的类,利用runtime的objc_getClassList或objc_copyClassList

    b、排除那些非继承自NSObject的类

    c、对过滤后的类缓存,以及类对应的签名也要进行缓存,这里是为了提高查找效率

2、在forwardInvocation里如下:

1 - (void)forwardInvocation:(NSInvocation *)anInvocation{
2     anInvocation.target = nil;
3     [anInvocation invoke];
4 }

这里利用了objc的对象值为null的对象调用任何方法都会不执行并不报错的特点,将调用者target的值设置为null即可。

下面是获取方法签名的具体代码:

 1 - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
 2     @synchronized([self class])
 3     {
 4         //look up method signature
 5         NSMethodSignature *signature = [super methodSignatureForSelector:aSelector];
 6         if (!signature)
 7         {
 8             //not supported by NSNull, search other classes
 9             static NSMutableSet *classList = nil;
10             static NSMutableDictionary *signatureCache = nil;
11             if (signatureCache == nil)
12             {
13                 classList = [[NSMutableSet alloc] init];
14                 signatureCache = [[NSMutableDictionary alloc] init];
15                 
16                 //get class list
17                 int numClasses = objc_getClassList(NULL, 0);
18                 Class *classes = (Class *)malloc(sizeof(Class) * (unsigned long)numClasses);
19                 numClasses = objc_getClassList(classes, numClasses);
20                 
21                 //add to list for checking
22                 c
23                 for (int i = 0; i < numClasses; i++)
24                 {
25                     //determine if class has a superclass
26                     Class someClass = classes[i];
27                     Class superclass = class_getSuperclass(someClass);
28                     while (superclass)
29                     {
30                         if (superclass == [NSObject class])
31                         {
32                             [classList addObject:someClass];
33                             break;
34                         }
35                         [excluded addObject:NSStringFromClass(superclass)];
36                         superclass = class_getSuperclass(superclass);
37                     }
38                 }
39                 
40                 //remove all classes that have subclasses
41                 for (Class someClass in excluded)
42                 {
43                     [classList removeObject:someClass];
44                 }
45                 
46                 //free class list
47                 free(classes);
48             }
49             
50             //check implementation cache first
51             NSString *selectorString = NSStringFromSelector(aSelector);
52             signature = signatureCache[selectorString];
53             if (!signature)
54             {
55                 //find implementation
56                 for (Class someClass in classList)
57                 {
58                     if ([someClass instancesRespondToSelector:aSelector])
59                     {
60                         signature = [someClass instanceMethodSignatureForSelector:aSelector];
61                         break;
62                     }
63                 }
64                 
65                 //cache for next time
66                 signatureCache[selectorString] = signature ?: [NSNull null];
67             }
68             else if ([signature isKindOfClass:[NSNull class]])
69             {
70                 signature = nil;
71             }
72         }
73         return signature;
74     }
75 }

 

posted @ 2019-12-30 16:35  zbblogs  阅读(1527)  评论(0编辑  收藏  举报