使用NSMethodSignature消息转发

消息转发是什么

在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消息转发)