美文网首页
问题:什么时候会报unrecognized selector的异

问题:什么时候会报unrecognized selector的异

作者: 姜小舟 | 来源:发表于2020-05-12 11:42 被阅读0次
  • 当调用该对象上某个方法,而该对象上没有实现这个方法的时候, 可以通过“消息转发”进行解决,如果还是不行就会报unrecognized selector异常

  • objc是动态语言,每个方法在运行时会被动态转为消息发送,即:objc_msgSend(receiver, selector),整个过程介绍如下:

  • objc在向一个对象发送消息时,runtime库会根据对象的isa指针找到该对象实际所属的类然后在该类中的方法列表以及其父类方法列表中寻找方法运行 如果,在最顶层的父类中依然找不到相应的方法时,程序在运行时会挂掉并抛出异常unrecognized selector sent to XXX 。但是在这之前,objc的运行时会给出三次拯救程序崩溃的机会:

    1. Method resolution

    调用resolveInstanceMethod:方法 (或 resolveClassMethod:)。允许用户在此时为该 Class 动态添加实现。如果有实现了,则调用并返回YES,那么重新开始objc_msgSend流程。这一次对象会响应这个选择器,一般是因为它已经调用过class_addMethod。如果仍没实现,继续下面的动作。

    + (BOOL)resolveInstanceMethod:(SEL)sel  __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0)
     {
        class_addMethod(self.class, sel, (IMP)dynamicMethodIMP, "@@:");
        BOOL rslt = [super resolveInstanceMethod:sel];
        rslt = YES;
        return rslt; // 1
    }
    
    2. Fast forwarding

    调用forwardingTargetForSelector:方法,尝试找到一个能响应该消息的对象。如果获取到,则直接把消息转发给它,返回非 nil 对象。否则返回 nil ,继续下面的动作。注意,这里不要返回 self ,否则会形成死循环。

    - (id)forwardingTargetForSelector:(SEL)aSelector  __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0) 
     {
         id rslt = [super forwardingTargetForSelector:aSelector];
         rslt = self.target;
         return rslt; // 2
     }
    
    3. Normal forwarding

    调用methodSignatureForSelector:方法,尝试获得一个方法签名。如果获取不到,则直接调用doesNotRecognizeSelector抛出异常。如果能获取,则返回非nil:创建一个 NSlnvocation 并传给forwardInvocation。
    调用forwardInvocation:方法,将上面获取到的方法签名包装成 Invocation 传入,如何处理就在这里面了,并返回非ni。

    //OBJC_SWIFT_UNAVAILABLE("")
    - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
       id rslt = [super methodSignatureForSelector:aSelector];
       NSMethodSignature *sig = [NSMethodSignature signatureWithObjCTypes:"v@:"];
       rslt = sig;
       return rslt; // 3
    }
    //OBJC_SWIFT_UNAVAILABLE("")
    
    (void)forwardInvocation:(NSInvocation *)anInvocation {
    // [super forwardInvocation:anInvocation];
    [self.target forwardInvocation:anInvocation];
    }
    

    调用doesNotRecognizeSelector: ,默认的实现是抛出异常。如果上面没能获得一个方法签名,执行该步骤。

    - (void)doesNotRecognizeSelector:(SEL)aSelector {
        // 在crash前 保存crash数据,供分析
        [super doesNotRecognizeSelector:aSelector]; // crash
    }
    

相关文章

网友评论

      本文标题:问题:什么时候会报unrecognized selector的异

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