“This is my fourth day of participating in the First Challenge 2022. For more details: First Challenge 2022.”

We used objc_msgSend to analyze IMP quick lookup flows, and we used objc_msgSend_uncached to analyze slow lookup flows, cached and cached. (The total length of this article is about 2071 words)

This paper focus on

  1. lookUpImpOrForwardThe slow lookup process and itsInternal detailsAnalysis.

objc_msgSend_uncached

We’ve looked at the logic of CacheHit in quick lookups, so we’ll look at the __objc_msgSend_uncached method, which we get when we don’t find it, to see how it gets handled internally:

  1. objc_msgSend_uncachedThe main logical content is inMethodTableLooupReady to findCls->method_list.
  2. Notice what’s in the commentbehaviorThe value of the argument to= = 3
  3. After the information is stored in the register, BL is entered_lookUpImpOrForwardSource code, start looking.

LookUpImpOrForward analysis

After locating the method in the C++ source code, look at the implementation logic in _lookUpImpOrForward:

As you can see, the main _lookUpImpOrForward code has been annotated. Now we’ll look at the overall logic and some local details:

  • The top half is zeroThe preparatory work: Create default IMP, check Cls for validity, implement Cls inheritance chain recursively and initializeMetaCls, prepare related classesrwTo find themethod_listTo prepare.
  • The bottom half is zeroJudge to find:
    1. realizeAndInitializeAll Cls associated with the current Cls have been initialized. The else.
      1. iOSWill enter assembly againcache_getImpLookup once, the internal logic is still fast lookupCacheLookup.
      2. Others go ingetMethodNoSuperIn theBinary traversalThe Clsmethod_list.
    2. Complete the if… Else if it still misses, at this pointCurCls==Cls.SuperCls, the parent class enters againcache_getImpLookup.
      1. Hit thego done
      2. A miss continues the loop
    3. The if logic outside the for loop is illustrated.

The overall logic is sorted out as above, and now we start to analyze the local details.

2.1 forward_imp

From our analysis of the overall flow, we can see that this IMP is the one that is returned after the entire inheritance chain has been found – until NSObject has not been found. In daily development, it looks like this on the console:

So the unrecognized… Is how to return to it, now enter the source code to see:

SQL > select * from ‘x17’ where ‘__objc_forward_imp’ is stored Then search _objc_forward_handler to see what the loaded handle is:

As shown in the figure, __OBJC2__ is a method distinction, return including class, metaclass identifier “+”, “-“, class name, etc., all concatenated, this is the daily development see the unrecognized – IMP.

2.2 checkIsKnownClass

Obj_allocateClassPair: Cls is created by obj_allocateClassPair: Cls is created by obj_allocateClassPair: Cls is created by obj_allocateClassPair. And the UINT16_t witness mark is stored in the Cache of Cls for verification.

  • If the witness command is used to query data, it is likely to return true.
  • If the judgment fails, check to saveallocatedClassesTable.

2.3 realizeAndInitializeIfNeeded_locked

This method mainly deals with CLS and clS. Superclss for the subsequent for loop. Now look at the internal implementation:

Its internal is mainly two if judgments, but also involves the implementation and initialization of the two chains in isa diagram:

  1. The first is judgmentcls->isRealizedThe judgment is based ondata()->flag31The purpose of the if inner method isImplement clS.rW on the current Cls and SuperCls chain.
    • For OC, methods in if are implemented concretelyrealizeClassWithoutSwiftThe analysis is then carried out.
    • The other branch is Swift.
  2. The second is judgmentcls->isInitializedThe judgment is based ondata()->flags29The purpose of the if inner method isInitialize the current Cls and Cls on the Metal chain
    • Methods in if are implemented concretelyinitializeNonMetaClassThe analysis is then carried out.

1. realizeClassWithoutSwift

  • Captures therealizeClassWithoutSwiftThe important part that can reflect its main logic, through the previous forAnalysis of De data Structure (General)The meaning of the code in the figure should be read from the text.

2. initializeNonMetaClass

  • Still interceptedinitializeNonMetaClassThe code that can represent its main logic, starting from the entry and now the SuperCls of the current Cls initialization, level by level initialization down, after initialization is complete, there is no problem then directlyreturn.

2.4 getMethodNoSuper_nolock

Next, look at how method_list looks in getMethodNoSuper_nolock:

Because method_array_t is two-dimensional structure, so here is a first layer for loop, further is the findMethodInSortedMethodList:

As shown in the figure, binary search is used to retrieve method_list_t, where >>1 is the key of binary and has been marked in the figure. The rest of the logic is relatively easy, with only two confusing points, as shown in the picture, to be explained in detail:

  1. After hitting a target SEL, why Probe forward to find the SEL with the same name and return to the first SEL?

    • Well, first of all, it says in the notes, thisProbe--And categories (Category(To be specificMethods with the same name in a category override methods in the main class. (Also, the current problem or the search algorithm in the figure can be explained in reverse:Why does a method of the same name in the last compiled class override a method in the main class)
    • Second, aren’t the main class methods overridden? Aren’t the methods of the category appended? How to haveProbe--Look ahead? The key to these questions is:Categories ofmethod_listTo join the main classmethod_listWhen the method ofattachLists()After opening a new array,method_listInsert the new list before the old list. (onattachListsThe analysis will be expanded when Category is analyzed later.)

    At first, I have not been aware of the Probe– but I have been aware of the Category through the annotations. When looking for a problem on the Internet, some blogs say that Probe– because the value of the Stack is FIFO. A class is a method that overrides the main class, and a class’s method is pushed back, so fetch it forward. Off the mark! Method_list_t inherits from entsize_list_tt, and subclasses property_list_t and ivar_list_t. The entsize_list_tt class is an iterator. The Generic implementation of **an array **of non-fragile structs. How can it have anything to do with Stack??

  2. If you miss SEL, why do Count–?

    • After that, you can subtract the current base location and move the probe position to 2/1 of the remaining lookups in the next loop.

    • Here’s a simple sketch to understand:

Third, summary


Above is the full analysis of the slow search process for objc_msgSend_uncached. In addition to the text analysis of the source logic, most of the source logic analysis is attached to the screenshots, and also needs to be looked at carefully.

At this point, OC message mechanism – all the source code about IMP lookup has been completely analyzed, and then into the analysis of the dynamic resolution part of the method.

Article analysis, if the content of the record is helpful, welcome to like, collect, comment. If not, please point out 🙆🏻♂️.