preface

OC Principle Exploration: objc_msgSend Process We have analyzed the objc_msgSend process. Objc_msgSend process is a fast lookup process for methods. Today we will explore the slow lookup process for methods.

The preparatory work

  • Objc4-818.2 – the source code

The assembly cache is not found

Assembly source back to C++ source

We explore the principles of OC: objc_msgSend process has said if no cache hit, will call __objc_msgSend_uncached function, we go to find this function in the source code.

  • in__objc_msgSend_uncachedFunctionMethodTableLookupAnd keep looking.

  • Found in the current fileMethodTableLookupAnd is found in the function_lookUpImpOrForward, to find_lookUpImpOrForwardFunction.

  • We looked globally, but we didn’t find it_lookUpImpOrForwardThe relevant definition, what do we do? Get rid of it_To findlookUpImpOrForward.

  • Succeed in findinglookUpImpOrForwardFunction, also declared fromCompile the source codeIt’s officially backC + + source code.

Why is the cache in assembly

1. The speed is fast

The purpose of caching is to improve efficiency, make assembly faster, make the current object find the cache faster, and optimize method lookup time.

2. The dynamic

Assembly is more dynamic. When we call a method, the parameters are unknown, and C++/C parameters are very specific

Second, slow search process preparation

Take a look at the overall structure of the lookUpImpOrForward function:

  • In the face of thelookUpImpOrForwardSome function calls to do the parsing.

checkIsKnownClass

Critical path for checkIsKnownClass:

  • Check the currentclassWhether the database is registered in the cache table.

realizeAndInitializeIfNeeded_locked

Let’s look at the implementation of the function:

  • There are two implementation paths in this function, and do some parsing for the key functions in the paths.

1. realizeClassWithoutSwift

Search path: RealizeClassMaybeSwiftAndLeaveLocked – > realizeClassMaybeSwiftMaybeRelock – > realizeClassWithoutSwift, do some instructions to the key function in the code.

  • Some of therwandroIn the operation.

  • Recursive searchclassandThe metaclassIf the initialization is not performed, perform the initialization.
  • What’s the point of doing this, because if the method is not found in the current class, it will goThe parent classLook up to the root class.

2. initialize

InitializeAndLeaveLocked -> initializeAndMaybeRelock -> initializeNonMetaClass -> callInitialize:

void callInitialize(Class cls)
{
    ((void(*)(Class, SEL))objc_msgSend)(cls, @selector(initialize));
    asm("");
}
Copy the code
  • We use them all the timeinitializeFunction, and here’s where to call it.

Three, two search process

At the core of the lookup process in lookUpImpOrForward is the following for loop:

  • Let’s look at the function.

Shared cache

  • Go to theShared cacheTo prevent temporary insertion methods.

Find the function getMethodNoSuper_nolock

Go to getMethodNoSuper_nolock:

  • The list of methods is a two-dimensional array that loops through the array, through the loopsearch_method_list_inlineFunction to go throughselTo find theimp.
  • To find theBinary searchMethods:search_method_list_inline -> findMethodInSortedMethodList -> findMethodInSortedMethodList, the following parsing.

Binary search findMethodInSortedMethodList

  • We assume that thecount = 16And began tofindMethodInSortedMethodListSource code analysis.
  • probe = base + (count >> 1);
    • probe = base + (16 >> 1) -> probe = base + (10000 >> 1) -> probe = base + 8, to get the middle of the list, because it’s a pan8Each unit is actually the first on the list9A location.
  • uintptr_t probeValue = (uintptr_t)getName(probe);
    • probeValue = The value in the middle.
  • if (keyValue == probeValue), the value in the middle and the value we’re looking forSELWhether the values are equal.
  • If equal:
    • I’m going to go ahead and find the first onecategoryMethods.
    • return &*probe;Returns the found method.
  • If not:
    • if (keyValue > probeValue)To determineSELIs the value greater thanThe median
    • ifSELBig:
      • base = probe + 1;.base = 8 + 1 = 9Translation,9Theta is actually the number one on the list10A location.
      • count--;.count = 15 = 1111.
      • count >>= 1.count = 7
    • ifSELSmall:
      • count >>= 1.count = count >> 1 = 8.

So that’s where the binary search algorithm is going to go, and then we’re going to continue to iterate, and it’s the same principle. If there are students still do not understand can use the following code for debugging, the code is my copy of the above source code.

Debug binary search algorithm

int search(int sel,int *a,int count); Int main (int arg c, const char * argv []) {@ autoreleasepool {int a [16] =,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16 {1}; int sel = 12; int index = search(sel, a, 16); NSLog(@"index:%d",index); } return 0; } int search(int sel,int *a,int count) { int *base = a; int *probe; for (; count ! = 0; count >>= 1) { probe = base + (count >> 1); int probeValue = *probe; if (sel == probeValue) { return probeValue; } if (sel > probeValue) { base = probe +1; count--; } } return -1; }Copy the code

Slow lookup recursive process

GetMethodNoSuper_nolock: getMethodNoSuper_nolock: getMethodNoSuper_nolock: getMethodNoSuper_nolock: getMethodNoSuper_nolock: getMethodNoSuper_nolock: getMethodNoSuper_nolock: getMethodNoSuper_nolock: getMethodNoSuper_nolock

Successfully find Method

If the Method is found successfully, it will then skip to done below via the goto statement.

  • indoneThe key approach inlog_and_fill_cache, click enter to view the implementation.

  • cache.insertFunction!!!!! Will be foundMethodInsert cache, returnimpThe search process ends.insertThe function goes backOC class principle exploration: Cache structure analysis supplementWhere we started, we closed the loop.

Method not found

If we don’t find a Method, then we go to the parent class and continue the exploration of the following process.

  • throughcurClass = curClass->getSuperclass()This code,curClassHas becomeThe parent class.
  • ifThe parent classfornil.imp = forward_imp;End,The for loop.
  • ifThe parent classDon’t fornil, the callcache_getImp(curClass, sel), the parent class for a quick cache lookup, the nextcache_getImpParse.

1. cache_getImp

Global search cache_getImp:

  • We found it didn’tcache_getImpThe realization, guess should be to go toassemblyLet’s look it up in assembly.

  • Successfully found in assembly_cache_getImpIn theOC principle exploration: objc_msgSend processIn which we have been on theCacheLookupIt was analyzed in detail. It wasQuickly findProcess, but it’s different here.
  • If the cache doesn’t find it, here’s theLGetImpMissDynamicIt’s going straight backnil, unlike the previous article is called__objc_msgSend_uncachedFunction.

2. Recursive invocation

Since cache_getImp returns nil, we continue through the for loop to the following code:

  • Continue togetMethodNoSuper_nolockFunction lookup method, but at this pointcurClassHas becomesuperClass.
  • If it is not found, the search will continuesuperClassuntilNSObjectThe parent classnil, at this momentimp = forward_impwillimpTo return.

Imp = forward_IMP, and what happens when imp is returned is explored in the next article.

Click a like to support it!! 😄 😄 😄