Method slow lookup process review

It was analyzed in the last articleMethod is a slow lookup processBut it leaves a point, if we don’t find itimpWhat’s going to happen? When not foundimp

Method can not find the error principle

First look at the source code

When no IMP is found, objc_msgForward_impcache is returned, and the global search is performed

foundobjc_msgForward_impcacheJump straight to__objc_msgForward

TailCallFunctionPinterIs to call the current$0The current$0forx17.x17for__objc_forward_handler, global searchobjc_forward_handler This is when we finally find when we don’t findimpError reporting method, I believe you should have encountered this kind of error. Under this supplementary explanation__attribute__

Here __attribute__ ((noreturn, cold))

  1. noreturnThis property tells the compiler that the function never returns a value. This property prevents error messages when a function needs to return a value but exits before it reaches the return value. The C library functions abort () and exit () are declared in this format
  2. coldThis means that the function is relatively obscure, so that the branch prediction mechanism will not prefetch it, or make it as obscure as the others(cold)So that it is more likely not to be put in the cache, and that the more popular instructions will be put in the cache. The opposite of that ishot.

Class object method dynamic resolution

From assembly source code

Can know behaviors = LOOKUP_INITIALIZW | LOOKUP_RESOLVER, Behavior ^ = LOOKUP_RESOLVER, call resolveMethod_locked(inst, sel, CLS, behavior); Static NEVER_INLINE IMP resolveMethod_locked(ID inst, SEL SEL, Class CLS, int behavior)

Follow our current processcls->isMetaClass()returnfalse, so I’m going to go inresolveInstanceMethod First of all, let’s look for it@selector(resolveInstanceMethod:).

IMP lookUpImpOrNilTryCache(id inst, SEL sel, Class cls, int behavior)
{
    return _lookUpImpTryCache(inst, sel, cls, behavior | LOOKUP_NIL);
}
Copy the code

When _lookUpImpTryCache is called, set it to LOOKUP_NIL, and attach the _lookUpImpTryCache source code

ALWAYS_INLINE static IMP _lookUpImpTryCache(id inst, SEL sel, Class cls, int behavior) { runtimeLock.assertUnlocked(); if(slowpath(! cls->isInitialized())) { // see comment in lookUpImpOrForward return lookUpImpOrForward(inst, sel, cls, behavior); } IMP imp = cache_getImp(cls, sel); if (imp ! = NULL) goto done; if (slowpath(imp == NULL)) { return lookUpImpOrForward(inst, sel, cls, behavior); } done: if ((behavior & LOOKUP_NIL) && imp == (IMP)_objc_msgForward_impcache) { return nil; } return imp; }Copy the code

So sel = @selector(resolveInstanceMethod), If the class implements @selector(resolveInstanceMethod) this class method (NSObject implements resolveInstanceMethod), call resolveInstanceMethod:, + (BOOL)resolveInstanceMethod (SEL)name + (BOOL)resolveInstanceMethod (SEL)name

+ (BOOL)resolveInstanceMethod:(SEL)sel
{
    if (sel == @selector(unimplementMethod))
    {
        IMP setHobby = class_getMethodImplementation(self, @selector(setHobby:));
        Method method = class_getInstanceMethod(self, @selector(setHobby:));
        const char *type = method_getTypeEncoding(method);
        return class_addMethod(self, sel, setHobby, type);
    }
    return NO;
}
Copy the code

After calling resolveInstanceMethod:, look up imp again

Class method dynamic resolution

As we can see from the previous summary, when CLS is metaClass, execute this code

else {
        // try [nonMetaClass resolveClassMethod:sel]
        // and [cls resolveInstanceMethod:sel]
        resolveClassMethod(inst, sel, cls);
        if (!lookUpImpOrNilTryCache(inst, sel, cls)) {
            resolveInstanceMethod(inst, sel, cls);
        }
    }
Copy the code

Now, you might wonder why you’re calling resolveInstanceMethod again, because the class method is stored in the metaclass, and the class method is the metaclass instanceMethod, so you need to call resolveInstanceMethod here. With this in mind, we can add an NSObject class to the resolve method, override the resolveInstanceMethod:, and dynamically add an IMP. You don’t need to implement resolveClassMethod as well, because you’ll find NSObject in the root metaclass.

conclusion

This article has analyzed only a small part of the message processing mechanism shown above, the dynamic resolution of class methods and instance methods. About the second halfforwardingSee the following article