“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
lookUpImpOrForward
The 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:
objc_msgSend_uncached
The main logical content is inMethodTableLooup
Ready to findCls->method_list
.- Notice what’s in the comment
behavior
The value of the argument to= = 3 - After the information is stored in the register, BL is entered
_lookUpImpOrForward
Source 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 initialize
MetaCls
, prepare related classesrw
To find themethod_list
To prepare. - The bottom half is zeroJudge to find:
realizeAndInitialize
All Cls associated with the current Cls have been initialized. The else.- iOSWill enter assembly again
cache_getImp
Lookup once, the internal logic is still fast lookupCacheLookup
. - Others go in
getMethodNoSuper
In theBinary traversalThe Clsmethod_list
.
- iOSWill enter assembly again
- Complete the if… Else if it still misses, at this point
CurCls==Cls.SuperCls
, the parent class enters againcache_getImp
Lookup.- Hit the
go done
- A miss continues the loop
- Hit the
- 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 save
allocatedClasses
Table.
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:
- The first is judgment
cls->isRealized
The judgment is based ondata()->flag
的31The purpose of the if inner method isImplement clS.rW on the current Cls and SuperCls chain.- For OC, methods in if are implemented concretely
realizeClassWithoutSwift
The analysis is then carried out. - The other branch is Swift.
- For OC, methods in if are implemented concretely
- The second is judgment
cls->isInitialized
The judgment is based ondata()->flags
的29The purpose of the if inner method isInitialize the current Cls and Cls on the Metal chain- Methods in if are implemented concretely
initializeNonMetaClass
The analysis is then carried out.
- Methods in if are implemented concretely
1. realizeClassWithoutSwift
- Captures the
realizeClassWithoutSwift
The 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 intercepted
initializeNonMetaClass
The 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:
-
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, this
Probe--
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 have
Probe--
Look ahead? The key to these questions is:Categories ofmethod_list
To join the main classmethod_list
When the method ofattachLists()
After opening a new array,method_list
Insert the new list before the old list. (onattachLists
The 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??
- Well, first of all, it says in the notes, this
-
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 🙆🏻♂️.