前言
在上一篇中,当缓存中方法找不到后,会调用_lookUpImpOrForward,从注释中可以看到是调用的lookUpImpOrForward方法,可以全局搜索找,也可以跟着断点找到lookUpImpOrForward在objc-runtime-new.mm中的源码。
一、lookUpImpOrForward源码解析
/*
*标准的IMP查找。
*没有LOOKUP_INITIALIZE:尝试避免+初始化(但有时会失败)
*不使用LOOKUP_CACHE:跳过乐观的未锁定查找(但在其他地方使用缓存)
*大多数呼叫者应使用LOOKUP_INITIALIZE和LOOKUP_CACHE
* inst是cls或其子类的实例;如果未知,则为nil。
*如果cls是未初始化的元类,则非nil inst会更快。
*可能会返回_objc_msgForward_impcache。供外部使用的IMP
*必须转换为_objc_msgForward或_objc_msgForward_stret。
*如果您根本不想转发,请使用LOOKUP_NIL。
*/
//behavior = LOOKUP_INITIALIZE | LOOKUP_RESOLVER
IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior)
{
const IMP forward_imp = (IMP)_objc_msgForward_impcache;
IMP imp = nil;
Class curClass;
runtimeLock.assertUnlocked();
// Optimistic cache lookup
if (fastpath(behavior & LOOKUP_CACHE)) {
imp = cache_getImp(cls, sel);
if (imp) goto done_nolock;
}
//在isRealized和isInitialized检查期间保持锁,以防止与并发实现竞争。我的理是锁realizeClassMaybeSwiftAndLeaveLocked和initializeAndLeaveLocked的。
//保证方法查找过程中method-lookup + cache-fill中方法添加的原子性。
// Otherwise, a category could be added but ignored indefinitely because the cache was re-filled with the old value after the cache flush on behalf of the category. ???????????不太理解这段,是在查找过程中找到了方法,然后在进入cache-fill前有一个同样的类别方法被动态地添加,导致cache-insert中存入的是旧方法的缓存,而类别的方法永远插入不进去缓存,所以加锁让lookup与cache-fill期间不可以添加类别方法??????????
runtimeLock.lock();
//检查类是不是在二进制文件创建或者通过objc_duplicateClass,objc_initializeClassPair或objc_allocateClassPair创建的,防御CFI攻击
checkIsKnownClass(cls);
if (slowpath(!cls->isRealized())) {
cls = realizeClassMaybeSwiftAndLeaveLocked(cls, runtimeLock);
// runtimeLock may have been dropped but is now locked again
}
if (slowpath((behavior & LOOKUP_INITIALIZE) && !cls