美文网首页iosObjecti...
使用NSMethodSignature消息转发

使用NSMethodSignature消息转发

作者: 骨古 | 来源:发表于2017-12-19 10:03 被阅读209次

    消息转发是什么

    在oc中调用方法就是发送消息(msgSend),如果给一个实例对象(Instance)发送一个未定义的消息,肯定会crash

    在VC里
        Person *p = [Person new];
        [p run];
    在Person类中
    run方法没有实现
    

    就会报这个错误

    unrecognized selector sent to instance 0x600000008310
    

    如果在运行时调用动态决议方法resolveInstanceMethod或者resolveClassMethod,决议失败,就是找不到,那么就会接着去调用methodSignatureForSelector,然后再调用forwardInvocation。如果对象没有重写这两个方法,就代表不支持方法转发调用,那么就会调用父类NSObject方法,NSObject父类方法forwardInvocation 中如下所示,所以导致异常,crash

    - (void) forwardInvocation: (NSInvocation*)anInvocation  
    {  
      [self doesNotRecognizeSelector:[anInvocation selector]];  
      return;  
    }  
    - (void) doesNotRecognizeSelector: (SEL)aSelector  
    {  
      [NSException raise: NSInvalidArgumentException  
               format: @"%s does not recognize %s",  
               object_get_class_name(self), sel_get_name(aSelector)];  
    }  
    

    下面是实现消息转发

    //方法签名 第三种
    - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
    {
        NSString *sel = NSStringFromSelector(aSelector);
        //手动生成签名
        if ([sel isEqualToString:@"run"]) {
            return [NSMethodSignature signatureWithObjCTypes:"v@:"];
        }else {
            return [super methodSignatureForSelector:aSelector];
        }
        
    }
    
    //拿到方法签名配发消息
    - (void)forwardInvocation:(NSInvocation *)anInvocation
    {
        NSLog(@"-----%@-----",anInvocation);
        //取得消息
        SEL selector = [anInvocation selector];
        //�转发
        SomePerson *someP = [SomePerson new];
        if ([someP respondsToSelector:selector]) {
            //调用对象,进行转发
            [anInvocation invokeWithTarget:someP];
        } else {
            
            return [super forwardInvocation:anInvocation];
        }
    }
    

    消息转发分为两步
    首先系统会调用- (id)forwardingTargetForSelector:(SEL)aSelector如果这个方法的返回值不是nil或者self,运行时系统会把消息发送给返回的哪个对象

    如果返回的是nil或者self,运行时系统首先会调用- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector来获得方法签名

    如果-methodSignatureForSelector返回的是nil,运行时系统会抛出unrecognized selector exception,程序到这里就结束了

    动态方法决议

    Objective C 提供了一种名为动态方法决议的手段,使得我们可以在运行时动态地为一个 selector 提供实现。我们只要实现 +resolveInstanceMethod: 或 +resolveClassMethod: 方法,并在其中为指定的 selector 提供实现即可(通过调用运行时函数 class_addMethod 来添加),并返回YES,运行时系统会重启一次消息的发送过程,调用动态添加的方法。例如,下面的例子

    void dynamicMethodIMP(id self, SEL _cmd) {
        // implementation ....
    }
    
    @implementation MyClass
    + (BOOL)resolveInstanceMethod:(SEL)aSEL
    {
        if (aSEL == @selector(resolveThisMethodDynamically)) {
              class_addMethod([self class], aSEL, (IMP) dynamicMethodIMP, "v@:");
              return YES;
        }
        return [super resolveInstanceMethod:aSEL];
    }
    @end
    

    class_addMethod 方法动态的添加新的方法与对应的实现,如果调用了[MyClass resolveThisMethodDynamically],将会转到动态添加的dynamicMethodIMP 方法中。Objective-C的方法本质上是一个至少包含两个参数(id self, SEL _cmd)的C函数,这样,当重启消息发送时,就能在类中找到@selector(dynamicMethodIMP)了。而如果方法返回NO时,将会进入下一步:消息转发(Message Forwarding)


    2016101401.png

    动态方法决议与消息转发

    相关文章

      网友评论

        本文标题:使用NSMethodSignature消息转发

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