The entrance

  • whenlookUpImpOrForwardthroughselTo find theimpCan’t find the
if (slowpath(behavior & LOOKUP_RESOLVER)) {
    behavior ^= LOOKUP_RESOLVER;
    return resolveMethod_locked(inst, sel, cls, behavior);
}

enum {
    LOOKUP_INITIALIZE = 1,
    LOOKUP_RESOLVER = 2,
    LOOKUP_NIL = 4,
    LOOKUP_NOCACHE = 8,
};
Copy the code
  • According to the compilation we see behavior = LOOKUP_INITIALIZE | LOOKUP_RESOLVER, sobehavior = 1 | 2 = 3
  • behavior & LOOKUP_RESOLVER = 3 & 2 = 2
  • behavior ^= LOOKUP_RESOLVER; = 2 ^ 2 = 0
  • behavior = 0When it’s on and other, it’s always 0, which is really just a tag, so this method only comes in once

ResolveMethod_locked analysis

static NEVER_INLINE IMP
resolveMethod_locked(id inst, SEL sel, Class cls, int behavior)
{
    runtimeLock.assertLocked();
    ASSERT(cls->isRealized());

    runtimeLock.unlock();

    // CLS is not a metaclass
    if (! cls->isMetaClass()) {
        // try [cls resolveInstanceMethod:sel]
        // Object method
        resolveInstanceMethod(inst, sel, cls);
    } 
    else {
       / / class methods
        // try [nonMetaClass resolveClassMethod:sel]
        // and [cls resolveInstanceMethod:sel]
        resolveClassMethod(inst, sel, cls);
        if (!lookUpImpOrNilTryCache(inst, sel, cls)) { resolveInstanceMethod(inst, sel, cls); }}// chances are that calling the resolver have populated the cache
    // so attempt using it
    // Try again
    return lookUpImpOrForwardTryCache(inst, sel, cls, behavior);
}
Copy the code

Object methods resolve dynamically

static void resolveInstanceMethod(id inst, SEL sel, Class cls) { runtimeLock.assertUnlocked(); ASSERT(cls->isRealized()); SEL resolve_sel = @selector(resolveInstanceMethod:); ResolveInstanceMethod () {return if (! lookUpImpOrNilTryCache(cls, resolve_sel, cls->ISA(/*authenticated*/true))) { // Resolver not implemented. return; BOOL (* MSG)(Class, SEL, SEL) = (typeof(MSG))objc_msgSend; bool resolved = msg(cls, resolve_sel, sel); // Cache the result (good or bad) so the resolver doesn't fire next time. // +resolveInstanceMethod adds to self a.k.a. IMP = lookUpImpOrNilTryCache(inST, sel, CLS); 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)); }}}Copy the code
  • If the lookup method is not found, as can be seen from the logic of the code above, we only need the object method implementedresolveInstanceMethod

  • Do not implementlookMethod, but we passresolveInstanceMethodThat will belooktheselBound toheartheimpon
  • The final call is going to behearmethods

Class method dynamic resolution

  • We know that class methods exist in metaclasses, and class methods are instance methods of metaclasses
  • So you can takeeatMethod dynamically resolves tosaymethods

NSObject+Catagory

  • Object methods -> Class -> Parent class > NSObject
  • Class methods -> metaclass -> parent -> root metaclass -> NSObject
  • I’m going to get there eventuallyNSOject, so we just need to implement it in the class of NSObjectresolveInstanceMethodYou can intercept all unimplemented onessel

  • As notcrash, the corresponding method is called
  • So you canhookAll unrealized hair method, processing analysis of it, can also prevent this crash, through this method to upload the crash information to the background