美文网首页
2022-08-08 记录一次线上崩溃 crash+[NSMet

2022-08-08 记录一次线上崩溃 crash+[NSMet

作者: 我是小胡胡分胡 | 来源:发表于2022-08-08 17:03 被阅读0次

    IOS 13.6.1 xsmax上出现。
    #43002 NSInvalidArgumentException
    +[NSMethodSignature signatureWithObjCTypes:]: type signature is empty.

    怀疑是下面的
    NSNull+NullSafe.m 实现有问题:

    
    
    #pragma GCC diagnostic ignored "-Wgnu-conditional-omitted-operand"
    
    @implementation NSNull (NullSafe)
    
    - (NSMethodSignature *)methodSignatureForSelector:(SEL)selector
    {
        @synchronized([self class])
        {
            //look up method signature
            NSMethodSignature *signature = [super methodSignatureForSelector:selector];
            if (!signature)
            {
                //not supported by NSNull, search other classes
                static NSMutableSet *classList = nil;
                static NSMutableDictionary *signatureCache = nil;
                if (signatureCache == nil)
                {
                    classList = [[NSMutableSet alloc] init];
                    signatureCache = [[NSMutableDictionary alloc] init];
                    
                    //get class list
                    int numClasses = objc_getClassList(NULL, 0);
                    Class *classes = (Class *)malloc(sizeof(Class) * (unsigned long)numClasses);
                    numClasses = objc_getClassList(classes, numClasses);
                    
                    //add to list for checking
                    NSMutableSet *excluded = [NSMutableSet set];
                    for (int i = 0; i < numClasses; i++)
                    {
                        //determine if class has a superclass
                        Class someClass = classes[i];
                        Class superclass = class_getSuperclass(someClass);
                        while (superclass)
                        {
                            if (superclass == [NSObject class])
                            {
                                [classList addObject:someClass];
                                break;
                            }
                            [excluded addObject:NSStringFromClass(superclass)];
                            superclass = class_getSuperclass(superclass);
                        }
                    }
                    
                    //remove all classes that have subclasses
                    for (Class someClass in excluded)
                    {
                        [classList removeObject:someClass];
                    }
                    
                    //free class list
                    free(classes);
                }
                
                //check implementation cache first
                NSString *selectorString = NSStringFromSelector(selector);
                signature = signatureCache[selectorString];
                if (!signature)
                {
                    //find implementation
                    for (Class someClass in classList)
                    {
     
                        if ([someClass instancesRespondToSelector:selector])
                        { 
                            signature = [someClass instanceMethodSignatureForSelector:selector];
                            break;
                        } 
                    }
                    
                    //cache for next time
     
                    signatureCache[selectorString] = signature ?: [NSNull null];
                }
                else if ([signature isKindOfClass:[NSNull class]])
                {
                    signature = nil;
                }
            }
            return signature; 
        }
    }
    
    - (void)forwardInvocation:(NSInvocation *)invocation
    {
        invocation.target = nil;
        [invocation invoke];
    }
    @end
    
    
    3265625-4328bd5c1b0f3368.png
    
    
    @implementation HelloClass
    
    void methodNormalc(void)
    {
        NSLog(@"methodNormalc");
        
    }
    
    
    //这里没啥用
    -(BOOL)respondsToSelector:(SEL)aSelector{
        bool a= [super respondsToSelector:aSelector];
        return a;
    }
    //如果方法没有实现,默认返回false
    //如果返回false,就会走消息转发
    +(BOOL)resolveInstanceMethod:(SEL)sel{
        bool a = [super resolveInstanceMethod:sel];
        //return true;     // 不管是true 还是 false ,都会继续往下转发,why
        NSLog(@"resolveInstanceMethod");
        
        
        if (sel==@selector(methodNormalc)) {
            
            class_addMethod(self, @selector(methodNormalc), methodNormalc, "v@:");
            
            // 动态添加了。方法之后,  不会在走消息转发的调用了, 就执行执行了!!
            // 不管下面返回的是false 还是。true
            //并且,添加了之后, 下次在调用这个methodNormalc 方法, 不会在走resolveInstanceMethod,而是直接就执行了methodNormalc 函数
            //return false;
            
            return  true; //但既然添加了,最好就是返回true吧, 但是也没有说明为啥这样更好!
        }
        return a;
    }
    //默认返回空
    //又被称为快速消息转发。
    // 如果为空,走慢速消息转发,继续转发消息
    -(id)forwardingTargetForSelector:(SEL)aSelector{
        id a = [super forwardingTargetForSelector:aSelector];
        NSLog(@"forwardingTargetForSelector");
        return a;
    }
    
    // 默认一般普通方法是返回空的。
    // 如果是协议方法,没有实现,不会反回空。
    //反回空,到这里就会崩溃了
    //如果这里返回了签名,会再次调用resolveInstanceMethod:(SEL)sel判断是否实现
    //如果仍然没有实现,就会走到fowardInvocation:
    -(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
        NSMethodSignature *a =[super methodSignatureForSelector:aSelector];
        NSLog(@"methodSignatureForSelector"); //如果签名为 空, 只接崩溃
        //如果是协议方法,  这个签名还是能返回的, 不会崩溃,可以走到forwardInvocation,再然后崩溃
        return a;
    }
    
    
    //默认实现是崩溃
    //并且不能用try-catch捕获
    -(void)forwardInvocation:(NSInvocation *)anInvocation{
        [super forwardInvocation:anInvocation];
        NSLog(@"forwardInvocation");
    }
    
    
    - (void)doesNotRecognizeSelector:(SEL)aSelector{
        NSLog(@"doesNotRecognizeSelector");
        //父类默认是崩溃
        [super doesNotRecognizeSelector:aSelector];
        NSLog(@"doesNotRecognizeSelector");
    }
    
    @end
    

    找到一个 nullsafe 其他实现:

    发现也有人发生crash
    https://github.com/nicklockwood/NullSafe/issues/24

    image.png

    相关文章

      网友评论

          本文标题:2022-08-08 记录一次线上崩溃 crash+[NSMet

          本文链接:https://www.haomeiwen.com/subject/lvgwwrtx.html