Object-c 消息转发机制(基于NSObject对象)

1. 什么叫做消息?
    对象调用某个方法就相当于给这个对象发送消息,如:
     [person run];//给person 对象发送名为“run”的消息,OC里任何的方法调用最终都会转化为obj_messageSend(对象,方法名,参数1,参数2,...)函数

2. 消息转发
     当给对象发送了一个不存在的消息时,默认会崩溃,提示“*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Person run]: unrecognized selector sent to instance”的错误
     此时可用NSObject里的一系列方法来处理此消息,共三套方案,系统默认从方案一执行到方案三,只要某一个方案处理了,后续方案就不会执行;这些方法是供系统调用的,不是供程序员调用的:
      方案一:
  • + (BOOL)resolveInstanceMethod:(SEL)sel  //实例方法的处理
  • + (BOOL)resolveClassMethod:(SEL)sel      //类方法的处理

     方案二:

  • - (id)forwardingTargetForSelector:(SEL)aSelector

     方案三:

  • - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector;   /实例方法的处理
  • + (NSMethodSignature *)instanceMethodSignatureForSelector:(SEL)aSelecto   //类方法的处理
  • - (void)forwardInvocation:(NSInvocation *)anInvocation;

    说明:

       一、【没有方法,我动态的给添加该方法】当对象或类没有方法A时,系统开始调用resolveInstanceMethod 或 resolveClassMethod方法给你一次机会,在此方法中,你可以动态的添加方法A的实现,采用运行时(class_addMethod),其目的仅仅只是给你一次机会来动态实现方法A(动态实现的方法,返回值,参数要保持一样),与其是否return yes还是no没有关系,如果你不实现,则挂

            屏幕快照 2015-03-21 下午7.31.52.png

       二、【真正的消息转发(快速)

               当不处理方案一,则开始执行forwardingTargetForSelector方法,可直接在此方法中返回一个其他对 象:

            - (id)forwardingTargetForSelector:(SEL)aSelector {
                    NSLog(@"---forwardingTargetForSelector");
                    return [[Car alloc] init];   //表示将aSelector这个方法(消息)给Car对象去处理,即调用Car对象中返回值、方法名、参数个数及类型一样的那个方法,此属性可以用来实现OC的伪多继承
}

      三、【慢速的消息转发

               当前面二者都处理时,开始执行如下两个函数 

  • - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector;   /实例方法的处理
  • + (NSMethodSignature *)instanceMethodSignatureForSelector:(SEL)aSelecto   //类方法的处理
  • - (void)forwardInvocation:(NSInvocation *)anInvocation;
  • 先调用生成一个方法签名,然后此方法签名给forwardInvocation函数使用,开头我们要找的错误unrecognized selector sent to instance原因,原来就是因为methodSignatureForSelector这个方法中,由于没有找到run对应的实现方法,所以返回了一个空的方法签名,然后在forwardInvocation中就产生崩溃了


    最后梳理下流程:

         当对象或类有方法A时,则走正常的消息传递流程

         当没有方法A时,按顺序执行那三个方案,只要某一个方案处理了,后续方案将不会执行。

            另外,之所以出现方法不存在时崩溃的提示,实质就是在forwardInvocation中使用了一个空的方法签名,然后就再调用doesNotRecognizeSelector方法打印信息。forwardInvocation 和 doesNotRecognizeSelector重写其中任意一个都可以实现调用不存在的方法导致程序崩溃的问题

方法声明

虽然上述机制可以转发当前类中没有实现的方法,但发送消息时仍然需要知道每个消息的方法签名,否则就会有编译器告警。可以通过category来声明转发消息的方法

参考:http://www.jianshu.com/p/1bde36ad9938

原文地址:https://www.cnblogs.com/cnsec/p/11515766.html