If an IMP is not found, the search process does not end. Instead, another process ———— slow search process begins. So let’s take a look at the slow lookup process of objc_msgSend through a series of analyses!

Links to the Runtime messaging series

[I] Analysis of the process of quick search for Runtime messages

[2] Analysis of Runtime message slow search process

[c] Runtime dynamic method resolution and message forwarding

Objc_msgSend Process Review

When sending messages, objc_msgSend (Receiver, _cmd) is called to send messages, where receiver is the receiver of messages and _cmd is the content of messages. As follows:

Person *p = [Person alloc];
[p test];

// Convert to CPP
Person *p = ((Person *objc_msgSend(objc_getClass("Person"), sel_registerName("alloc")); 
objc_msgSend(p, sel_registerName("test"));    
Copy the code

1. Objc_msgSend Quick lookup review

1. Find ISA by class

Cache_t = isa; cache_t = isa

Then grab buckets for the storage method and try to grab IMP from bucket via hash array

4. Return if found, or continue traversing the other buckets if not found

5, finally enter the slow search process.

2. Objc_msgSend Slow search

Here, we start with a function ———— lookUpImpOrForward, which we trace back to in assembly analysis of fast lookups and start another journey of message lookups ———— slow lookups!

Objc_msgSend slow lookup parsing

LookUpImpOrForward method

  • The following program is an implementation of lookUpImpOrForward in the Runtime. Refer to the comments for details:

#pragmaMark - Message lookup process, core content

NEVER_INLINE
IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior)
{
#pragmaMark - Look for IMP from _objc_msgForward_impcache
    const IMP forward_imp = (IMP)_objc_msgForward_impcache;
    IMP imp = nil;
    Class curClass;

    runtimeLock.assertUnlocked(a);#pragmaMark - On the first initialization, mark the class as not Initialized yet
    if (slowpath(! cls->isInitialized())) {
        behavior |= LOOKUP_NOCACHE;
    }

    runtimeLock.lock(a);#pragmaMark - Checks whether the class has done an Initialized binding
    checkIsKnownClass(cls);
    cls = realizeAndInitializeIfNeeded_locked(inst, cls, behavior & LOOKUP_INITIALIZE);
    runtimeLock.assertLocked(a); curClass = cls;#pragmaMark - Finds the method of the current class through the for loop
    for (unsigned attempts = unreasonableClassCount();;) {
        if (curClass->cache.isConstantOptimizedCache(/* strict */true)) {
#if CONFIG_USE_PREOPT_CACHES


#pragmaMark - left left left left left left left left left left down down down down down down down down down left left left left left left left left left left down down down down down down down down down left left left left left left left left left left down down down down down down down down down left left left left left left left left left down down down down down down down down left left left left left left down down down down down
#pragmaMark - Start looking for methods in the cache and method list

            imp = cache_getImp(curClass, sel);
            if (imp) goto done_unlock;
            curClass = curClass->cache.preoptFallbackClass(a);#endif
        } else {

#pragmaMark - Find methods in the list of methods in the current class (binary search method to find methods)
            Method meth = getMethodNoSuper_nolock(curClass, sel);
            if (meth) {
                imp = meth->imp(false);
                goto done;
            }

#pragmaMark - No method found and no subsequent processing, out of the loop
            if (slowpath((curClass = curClass->getSuperclass()) == nil)) {
                imp = forward_imp;
                break; }}#pragmaMark - The number of binary lookup attempts for the parent class ends, sending an error notification
        if (slowpath(--attempts == 0)) {
            _objc_fatal("Memory corruption in class list.");
        }

#pragmaMark - left left left left left left left left left left down down down down down down down down down left left left left left left left left left left down down down down down down down down down left left left left left left left left left left down down down down down down down down down left left left left left left left left left down down down down down down down down left left left left left left down down down down down
#pragmaMark - This class could not find a method and started looking for methods in the parent class's cache and method list
        imp = cache_getImp(curClass, sel);
        if (slowpath(imp == forward_imp)) {
#pragmaMark - Finds AN IMP in the cache and jumps out of the current loop to process the IMP
            break;
        }
        
        if (fastpath(imp)) {
#pragmaMark - Finds that the IMP is in the parent class, caches it into its own class information, and then processes the IMP
            gotodone; }}#pragmaMark - If imp is not implemented, then enter the message dynamic parsing process, if implemented, no processing, continue to follow up
    if (slowpath(behavior & LOOKUP_RESOLVER)) {
        behavior ^= LOOKUP_RESOLVER;
        return resolveMethod_locked(inst, sel, cls, behavior);
    }

#pragmaMark - left left left left left left left left left left down down down down down down down down down left left left left left left left left left left down down down down down down down down down left left left left left left left left left left down down down down down down down down down left left left left left left left left left down down down down down down down down left left left left left left down down down down down
#pragmaMark-************** Core transaction process ************
#pragmaMark - left left left left left left left left left left down down down down down down down down down left left left left left left left left left left down down down down down down down down down left left left left left left left left left left down down down down down down down down down left left left left left left left left left down down down down down down down down left left left left left left down down down down down

/* * done done: * 1, if there is IMP in cache, return nil */ 2, print and insert method into current class information */ 3, if there is no cache or method like IMP found, return nil */

 done:
    if (fastpath((behavior & LOOKUP_NOCACHE) == 0)) {
#if CONFIG_USE_PREOPT_CACHES
        while (cls->cache.isConstantOptimizedCache(/* strict */true)) {
            cls = cls->cache.preoptFallbackClass(a); }#endif
        log_and_fill_cache(cls, imp, sel, inst, curClass);
    }
 done_unlock:
    runtimeLock.unlock(a);if (slowpath((behavior & LOOKUP_NIL) && imp == forward_imp)) {
        return nil;
    }
    return imp;
}

Copy the code

2. Flow chart of message slow search

3. Key points in the search process

Fast search process we analyzed the assembly related implementation, so slow search is also related to the difficulties exist. For example, in the process of finding a method, what is the basis of the upper level search, search algorithm implementation. So let’s look at these key code implementations!

Analysis of key points in the search process

3.1. How do I find cached methods in a class?

        meth = _cache_getMethod(curClass, sel, _objc_msgForward_impcache);
Copy the code
  • We can’t find the implementation of _cache_getMethod in runtime c++ code, so we search for the implementation of assembly:
#pragmaMark goes through isa->class->cache_t->buckets->bucket->SEL IMP
#pragmaMark goes through isa->class->cache_t->buckets->bucket->SEL IMP
#pragmaMark goes through isa->class->cache_t->buckets->bucket->SEL IMP

1: cmp    p0, #0

2: GetClassFromIsa_p16 p13, 1, x0    // p16 = class

3: CacheLookup NORMAL, _objc_msgSend, __objc_msgSend_uncached

3.1 : LLookupStart -> CACHE_MASK_STORAGE_HIGH_16

/ / get the cache
ldr    p11, [x16, #CACHE]            // p11 = mask|buckets

CONFIG_USE_PREOPT_CACHES == 1

// Whether position 0 of P11 is 0 No -> LLookupPreopt
tbnz    p11, #0, LLookupPreopt\Function
and    p10, p11, #0x0000ffffffffffff    // p10 = buckets

// p1 sel >> 7 == value ^= value >> 7;
eor    p12, p1, p1, LSR #7
/ / the hash index
and    p12, p12, p11, LSR #48        // x12 = (_cmd ^ (_cmd >> 7)) & mask

// index << 4
/ / 2 * 16
// buckets + 32
P13 bucket(sel imp)
add    p13, p10, p12, LSL #(1+PTRSHIFT)
                    // p13 = buckets + ((_cmd & mask) << (1+PTRSHIFT))
// sel -> imp

Copy the code

3.2. How to find a method in the list of methods in a class?

  • During the search, the following methods are called to perform the method search.

#pragmaMark - Find methods in the list of methods in the current class (binary search method to find methods)

 Method meth = getMethodNoSuper_nolock(curClass, sel);

Copy the code


  • GetMethodNoSuper_nolock internally implements the CLS ->data()->methods() procedure.

static method_t *
getMethodNoSuper_nolock(Class cls, SEL sel)
{...auto const methods = cls->data() - >methods(a);for (auto mlists = methods.beginLists(),
              end = methods.endLists(a); mlists ! = end; ++mlists) {method_t *m = search_method_list_inline(*mlists, sel);
        if (m) return m;
    }
    return nil;
}
Copy the code

Iv. Summary of overall thinking:

1, first check whether the class exists, does not exist, return null, otherwise continue,

2, find the class’s cache, IMP return, otherwise continue,

3, find class’s method lists, return IMP if found, otherwise continue,

4, search the superclass cache (superclass) and method lists, find return IMP, otherwise continue

5, if you find IMP, and IMP is not the IMP of this class, the parent class OF IMP cache copy to their own cache next use,

6, if there is no IMP, then the dynamic method resolution and message forwarding process is performed.

The above procedure is a summary of the slow search process of the whole method.