前面的文章我们谈到了objc_msgSend的快查找和慢查找流程,其实就是在缓存和类、父类、元类里去找imp,如果还是没找到,系统会给应用程序一次"补救"机会,在lookUpImpOrForward函数中会进入动态方法决议。以下截图为入口。
经过验证,在方法决议时会三次执行6190行,resolveInstanceMethod方法也会被调用两次,原因是:
第一次会正常进入resolveMethod_locked,再进入resolveInstanceMethod函数,到达6020行的时候调用lookUpImpOrNil函数,又会第二次回到动态方法决议入口,由于位运算操作,第二次不会进入。在resolveMethod_locked函数结尾处又会调用lookUpImpOrForward,这是第三次调用,此时能够进入6192行resolveMethod_locked函数中。
既然在一次方法决议中resolveInstanceMethod会被调用两次,而且它也会被系统其它方法调用到,那么我们在实现这个方法的时候需要做好判断。通常会在实现里,动态为类添加class_addMethod一个“合适”的方法,以防崩溃。
在resolveMethod_locked函数里6063和6065行,这里会先调用resolveClassMethod,然后再调用resolveInstanceMethod,为什么会调用两次?
类方法是以对象(实例)方法存在于元类中,可以结合isa走位图进行理解,这篇文章里也会说明。所以这里还要进行resolveInstanceMethod。我们无法直接拿到元类,但是我们可以通过isa走位图的继承链知道,所有元类的根父类都是NSObject,所以这里可以新建NSObject分类并重写resolveInstanceMethod或者resolveClassMethod方法来处理动态消息决议。
如果没有处理动态消息决议,则会进入消息转发流程,消息转发有两个重要函数:
1、快速消息转发:forwardingTargetForSelector 将消息转发给其他对象来处理,也可以转发给自己并class_addMethod等操作;
2、慢速消息转发:methodSignatureForSelector 需要返回一个NSMethodSignature函数签名,完成这个签名就会将消息转发给forwardInvocation函数来处理,慢速消息转发处理拥有的权限会更大更多,我们可以指定NSInvocation的target和selector后,进行invoke等操作;
我们如何正面推导出这个流程呢?CoreFoundition里!!!通过反汇编得到。。。。。。未完待续。。。。。。