动态方法决议
我们全局搜索lookUpImpOrForward,最后在objc-runtime-new.mm文件中找到了源码实现,这是一个c实现的函数,源码如下:


我们发现源码中有个resolveMethod_locked,点击进入发现源码如下:
static NEVER_INLINE IMP
resolveMethod_locked(id inst, SEL sel, Class cls, int behavior)
{
runtimeLock.assertLocked();
ASSERT(cls->isRealized());
runtimeLock.unlock();
// 动态方法决议 : 给一次机会 重新查询
if (! cls->isMetaClass()) { // 对象 - 类
// try [cls resolveInstanceMethod:sel]
resolveInstanceMethod(inst, sel, cls);
}
else { // 类方法 - 元类
// try [nonMetaClass resolveClassMethod:sel]
// and [cls resolveInstanceMethod:sel]
resolveClassMethod(inst, sel, cls);
if (!lookUpImpOrNil(inst, sel, cls)) { // 为什么要有这行代码
resolveInstanceMethod(inst, sel, cls);
}
}
// chances are that calling the resolver have populated the cache
// so attempt using it
return lookUpImpOrForward(inst, sel, cls, behavior | LOOKUP_CACHE);
}
我们发现如果是元类的话就走resolveClassMethod,不是元类的话就走resolveInstanceMethod,接下来代码分析
代码分析
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
LGPerson *person = [LGPerson alloc];
[LGPerson sayNB];
}
return 0;
}
如果这个方法没有实现,会报错误如下:

实现方案
我们在LGPerson这个类中实现如下代码:
+ (BOOL)resolveClassMethod:(SEL)sel{
NSLog(@"%@ 来了",NSStringFromSelector(sel));
if (sel == @selector(sayNB)) {
IMP imp = class_getMethodImplementation(objc_getMetaClass("LGPerson"), @selector(lgClassMethod));
Method sayMMethod = class_getInstanceMethod(objc_getMetaClass("LGPerson"), @selector(lgClassMethod));
const char *type = method_getTypeEncoding(sayMMethod);
return class_addMethod(objc_getMetaClass("LGPerson"), sel, imp, type);
}
return [super resolveClassMethod:sel];
}
其实我们之前的探索以及isa的走位图,我们可以发现类方法存在元类中,实际上也是元类中的实例方法。
消息转发机制

我们通过图1可以发现在执行doesNotRecognizedSelector之前,执行forwarding_prep_0和forwarding。
那么接下我们要去寻找forwarding_prep_0和forwarding,我们下载CoreFoundation开源了的代码里面查找,发现找不到。
接下来通过image list指令查看所有的编译文件
我们找到CoreFoundation的编译文件路径:/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation

接下来我们通过hopper软件对这个CoreFoundation编译文件进行反汇编


在forwarding的伪代码里面,我们看到:
先找forwardingTargetForSelector,
如果找到forwardingTargetForSelector,消息转发
如果没有找到forwardingTargetForSelector,就会找methodSignatureForSelector
如果没有找到methodSignatureForSelector,直接unrecognized selector
如果找到methodSignatureForSelector,会继续寻找forwardInvocation,
如果没有找到forwardInvocation,直接unrecognized selector
如果找到forwardInvocation,消息转发。
网友评论