解決因為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 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM