OC中的方法调用, 其实都是转换为objc_msgSend
函数的调用
objc_msgSend
的执行流程可以分为3大阶段
- 消息发送
- 动态方法解析
- 消息转发
源码是用汇编写的, 但可以大致看一下:
方法调用最后走到了缓存查找里面:
CacheLookup
如果方法在缓存中没有找到, 就会调用: __objc_msgSend_uncached
我们看看
MethodTableLookup
里面发生了什么:
调用了_lookUpImpOrForward
, 意思是找到这个函数的指针, 然后我们在这个类objc-runtime-new
中找到了它的方法实现:
在这个方法里, 它先去当前类的缓存里找方法, 如果找到, 直接返回
imp = cache_getImp(curClass, sel);
if (imp) goto done_unlock;
如果缓存里没有, 就去当前类的方法列表里找getMethodNoSuper_nolock
找到了就继续走到
done
那里:这里会将找到的方法缓存到当前类:
log_and_fill_cache(cls, imp, sel, inst, curClass);
如果找不到, 就会去父类的缓存里找
如果找到了, 就缓存到当前类(不是找到的父类)
如果没找到, 就继续在父类的方法列表里找.
如此循环往复, 如下图所示:
经过这么一个过程, 如果找到了, 也就是这个汇编方法MethodTableLookup
将存在于x0
寄存器中的函数指针移动到x17
寄存器
也就是说, 现在
x17
寄存器里存有了函数指针, 然后调用TailCallFunctionPointer
如果经过整个一个过程都没有找到方法IMP
, 那么就会进入下一个阶段:
动态方法解析
注释中是这么说的:
No implementation found. Try method resolver once
找不到方法, 尝试一次方法解析, 那么resolveMethod_locked
又做了什么呢?
如果这个类不是元类对象,动态方法解析就走到这里resolveInstanceMethod
,如果是元类对象,先调用resolveClassMethod
比如我在一个Person
类中实现resolveInstanceMethod
:
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == @selector(run)) {
return YES;
}
return [super resolveInstanceMethod:sel];
}
我开始以为只要实现了这个类, 返回YES
,就可以了, 但事实上还是会报经典的找不到方法的错误. 所以还是得真正去解析, 为什么呢?
以为最终还是要调用lookUpImpOrForwardTryCache
- (void)other {
NSLog(@"%s", __func__);
}
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == @selector(run)) {
Method otherMethod = class_getInstanceMethod([Person class], @selector(other));
class_addMethod([Person class], sel, method_getImplementation(otherMethod), method_getTypeEncoding(otherMethod));
return YES;
}
return [super resolveInstanceMethod:sel];
}
例如, 动态添加了一个方法, 而添加的方法是确实存在的. 这样才是一个真正的动态方法解析. 整个流程如下: