iOS 消息转发

527 阅读3分钟

接上篇方法查找流程

动态决议

一个方法调用后,经过汇编快速查找,类方法列表查找,循环遍历父类的方法列表查找,这一番查找后都没有找到,这个时候会进行下一步,动态解析方法

 // No implementation found. Try method resolver once.
if (resolver  &&  !triedResolver) {
    runtimeLock.unlock();
    _class_resolveMethod(cls, sel, inst);
    runtimeLock.lock();
    // Don't cache the result; we don't hold the lock so it may have 
    // changed already. Re-do the search from scratch instead.
    triedResolver = YES;
    goto retry;
}

这一步核心代码为 _class_resolveMethod(cls, sel, inst); 查看内部实现

void _class_resolveMethod(Class cls, SEL sel, id inst)
{
//判断当前类不是元类,也就对象方法
if (! cls->isMetaClass()) {
    // try [cls resolveInstanceMethod:sel]

    _class_resolveInstanceMethod(cls, sel, inst);
} 
else {
    // try [nonMetaClass resolveClassMethod:sel]
    // and [cls resolveInstanceMethod:sel]
    _class_resolveClassMethod(cls, sel, inst); // 已经处理
    if (!lookUpImpOrNil(cls, sel, inst, 
                        NO/*initialize*/, YES/*cache*/, NO/*resolver*/)) 
    {
        // 对象方法 决议
        _class_resolveInstanceMethod(cls, sel, inst);
    }
}
}

首先,判断当前类不是元类,也就对象方法,也就是会执行 _class_resolveInstanceMethod(cls, sel, inst),查看内部实现

 static void _class_resolveInstanceMethod(Class cls, SEL sel, id inst)
{
if (! lookUpImpOrNil(cls->ISA(), SEL_resolveInstanceMethod, cls, 
                     NO/*initialize*/, YES/*cache*/, NO/*resolver*/)) 
 {
    // Resolver not implemented.
    return;
 }

// 系统给你一次机会 - 你要不要针对 sel 来操作一下下
BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
bool resolved = msg(cls, SEL_resolveInstanceMethod, sel);

// Cache the result (good or bad) so the resolver doesn't fire next time.
// +resolveInstanceMethod adds to self a.k.a. cls
IMP imp = lookUpImpOrNil(cls, sel, inst, 
                         NO/*initialize*/, YES/*cache*/, NO/*resolver*/);

if (resolved  &&  PrintResolving) {
    if (imp) {
        _objc_inform("RESOLVE: method %c[%s %s] "
                     "dynamically resolved to %p", 
                     cls->isMetaClass() ? '+' : '-', 
                     cls->nameForLogging(), sel_getName(sel), imp);
    }
    else {
        // Method resolver didn't add anything?
        _objc_inform("RESOLVE: +[%s resolveInstanceMethod:%s] returned YES"
                     ", but no new implementation of %c[%s %s] was found",
                     cls->nameForLogging(), sel_getName(sel), 
                     cls->isMetaClass() ? '+' : '-', 
                     cls->nameForLogging(), sel_getName(sel));
    }
}
}

当我们调用了一个找不到方法,系统让我们可以主动响应

+(BOOL)resolveInstanceMethod:(SEL)sel,我们可以处理下找不到的sel,系统在调用这个方法后,会再次查找下当前sel imp的实现,这个过程中我们如果给他添加一个imp,下一步就是找到了,那么就不会报错

IMP sayHIMP = class_getMethodImplementation(self, @selector(sayMaster));

 Method sayHMethod = class_getInstanceMethod(self, @selector(sayMaster));
       
 const char *sayHType = method_getTypeEncoding(sayHMethod);
        
return class_addMethod(self, sel, sayHIMP, sayHType);

动态决议后,开始进入消息转发流程

快速转发

我们写一个扩展方法,

extern void instrumentObjcMessageSends(BOOL flag);

打印出来系统的调用方法

打开finder,前往 /tmp/msgSend

查看打印的文件

resolveInstanceMethod:

已经分析过了,还有两个陌生的函数

forwardingTargetForSelector:
methodSignatureForSelector:

我们进入系统文档搜索下这个函数

文档的解释是,返回一个可以响应该sel的对象,也就是可以吧这个sel的实现转发给一个指定的对象来解决无法找到imp的实现的问题

// 快速转发 - 交给其他对象来处理
//- (id)forwardingTargetForSelector:(SEL)aSelector{
//    NSLog(@"%s -- %@",__func__,NSStringFromSelector(aSelector));
//    if (aSelector == @selector(saySomething)) {
//        return [LGTeacher alloc];
//    }
//    return [super forwardingTargetForSelector:aSelector];
//}

那么还有一个最后的流程,就是慢速转发了(方法签名) 文档搜索

文档解释:返回一个NSMethodSignature对象,该对象包含由给定选择器标识的方法的描述。

// 慢速转发 
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
NSLog(@"%s -- %@",__func__,NSStringFromSelector(aSelector));
if (aSelector == @selector(saySomething)) { // v @ :
    return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
return [super methodSignatureForSelector:aSelector];
}

//
- (void)forwardInvocation:(NSInvocation *)anInvocation{
NSLog(@"%s ",__func__);

   // 事情 - 事务 - 秘书 - 失效
     // 系统本质
  //   SEL aSelector = [anInvocation selector];
  //
   //   if ([[LGTeacher alloc] respondsToSelector:aSelector])
   //       [anInvocation invokeWithTarget:[LGTeacher alloc]];
 //   else
//       [super forwardInvocation:anInvocation];
}

通过慢速转发后,所有未处理的事物会进入forwardInvocation,我们可以在这里进行事物的处理,比如收集等等

最后附上消息转发的流程图