OC 的Runtime 消息转发机制

 runtime 它简单的说就是一种运行时特性,OC 是一种动态性特别强的语言,比如方法的调用等都在程序运行的时候才决定,这也造就了他比较灵活的特性,一些 hack等黑魔法的操作。

OC的方法调用,称为消息发送,就是给被调用的对象发送了一条消息 ,像这样【receiver msg】其实就是告诉对象要调用某个方法(其实就是函数),每一个方法都对应 SEL,这个 SEL叫做选择器,表示一个方法的 selector指针,两个类之间不管之间有什么关系,只要方法名字相同,那么就对应了同一个 SEL,所以在 OC 中同一个类或者同一个继承体系中,不能存在两个相同的方法名,即使参数类型不同也不行。不同类的实例对象执行相同的 selector 时,会在各自的 methodList 中寻找各自对应的 IMP ,那么这个 IMP 是什么鬼,那就说说吧,IMP 其实就相当于函数指针,指向方法实现的首地址,IMP 就是为 SEL 而生的,SEL 是一个方法的唯一标识,通过取得 IMP,我们可以跳过 OC 的 Runtime 机制,直接指向 IMP 的方法实现,这样就省去了 runtime 过程中的一系列的消息查找工作,会比直接向对象发送消息更加高效一些。

向一个对象发送一条消息 例如:

[array insertObject:foo atIndex:2];
//会被翻译成:
objc_msgSend(array, @selector(insertObject:atIndex), foo, 2);
objc_msgSend()做了哪些事呢?
1> 通过 isa指针找到 class
2> 在 class 的 method_list 中找到 foo方法
3> 如果该 class 中没有找到 继续去他的 superClass 中找
4> 如果找到就去执行对应的实现(IMP)
如果没有找到 并不会马上 crash , 还有两重的错误处理
 
那么下面就是消息转发了,当 OC 向某个对象发送了一条未知的消息时,他并不会马上报错,而是会经历2个步骤:
1> Dynamic Method Resolution(动态方法决议)
2> Message Forwarding(消息转发)

// 动态方法决议

如果调用的方法是实例方法,OC的运行时会调用- (BOOL)resolveInstanceMethod:(SEL)sel,如果是类方法,则会调用+ (BOOL)resolveClassMethod:(SEL)sel 让我们可以在程序运行时动态的为一个selector提供实现,如果我们添加了函数的实现,并返回YES,运行时系统会重启一次消息的发送过程,调用动态添加的方法。

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

(1)动态方法解析:首先调用所属类的的类方法+resolveInstanceMrthod 可以有机会为该未知消息新增一个处理方法,不过前提是我们已经实现了这个处理方法,我们可以通过 class_addMethod 函数动态的添加未知消息到类里,让原来没有处理这个消息的类具有处理这个消息的能力

(2)完整消息转发:

  • 首先运行时系统会调用- (id)forwardingTargetForSelector:(SEL)aSelector方法,如果这个方法中返回的不是nil或者self,运行时系统将把消息发送给返回的那个对象

  • 如果- (id)forwardingTargetForSelector:(SEL)aSelector返回的是nil或者self,运行时系统首先会调用- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector方法来获得方法签名,方法签名记录了方法的参数和返回值的信息,如果-methodSignatureForSelector 返回的是nil, 运行时系统会抛出unrecognized selector exception,程序到这里就结束了。

要实现多重代理的分发,我们需要让Runtime系统运行到forwardInvocation这一步,并在该方法中将delegate方法分发到其他各个对象中去:

下面看例子 分别有两个类 Boy 和 Girl 类
分别有 say 的方法  让 girl 类调用了一个她自己没有的方法
1>>> 动态添加方法实现

- (void)viewDidLoad {

    [super viewDidLoad];

    Girl *girl = [[Girl alloc]init];

    [girl performSelector:@selector(checkMessageInvocation) withObject:nil];

}

2>>> 消息备选者 可以将这个消息转发给另一个对象 接受的对象实现即可  如果 return nil 则进入最后一步的消息转发

3>>> 消息转发

原文地址:https://www.cnblogs.com/ChrisZhou666/p/6249780.html