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_uncached
FunctionMethodTableLookup
And keep looking.
- Found in the current file
MethodTableLookup
And is found in the function_lookUpImpOrForward
, to find_lookUpImpOrForward
Function.
- We looked globally, but we didn’t find it
_lookUpImpOrForward
The relevant definition, what do we do? Get rid of it_
To findlookUpImpOrForward
.
- Succeed in finding
lookUpImpOrForward
Function, also declared fromCompile the source code
It’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 the
lookUpImpOrForward
Some function calls to do the parsing.
checkIsKnownClass
Critical path for checkIsKnownClass:
- Check the current
class
Whether 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 the
rw
andro
In the operation.
- Recursive search
class
andThe metaclass
If 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 go
The parent class
Look 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 time
initialize
Function, 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 the
Shared cache
To 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 loop
search_method_list_inline
Function to go throughsel
To find theimp
. - To find the
Binary search
Methods:search_method_list_inline
->findMethodInSortedMethodList
->findMethodInSortedMethodList
, the following parsing.
Binary search findMethodInSortedMethodList
- We assume that the
count = 16
And began tofindMethodInSortedMethodList
Source 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 pan8
Each unit is actually the first on the list9
A 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 forSEL
Whether the values are equal.- If equal:
- I’m going to go ahead and find the first one
category
Methods. return &*probe;
Returns the found method.
- I’m going to go ahead and find the first one
- If not:
if (keyValue > probeValue)
To determineSEL
Is the value greater thanThe median
- if
SEL
Big:base = probe + 1;
.base = 8 + 1 = 9
Translation,9
Theta is actually the number one on the list10
A location.count--;
.count = 15 = 1111
.count >>= 1
.count = 7
- if
SEL
Small: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.
- in
done
The key approach inlog_and_fill_cache
, click enter to view the implementation.
cache.insert
Function!!!!! Will be foundMethod
Insert cache, returnimp
The search process ends.insert
The 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.
- through
curClass = curClass->getSuperclass()
This code,curClass
Has becomeThe parent class
. - if
The parent class
fornil
.imp = forward_imp;
End,The for loop
. - if
The parent class
Don’t fornil
, the callcache_getImp(curClass, sel)
, the parent class for a quick cache lookup, the nextcache_getImp
Parse.
1. cache_getImp
Global search cache_getImp:
- We found it didn’t
cache_getImp
The realization, guess should be to go toassembly
Let’s look it up in assembly.
- Successfully found in assembly
_cache_getImp
In theOC principle exploration: objc_msgSend processIn which we have been on theCacheLookup
It was analyzed in detail. It wasQuickly find
Process, but it’s different here. - If the cache doesn’t find it, here’s the
LGetImpMissDynamic
It’s going straight backnil
, unlike the previous article is called__objc_msgSend_uncached
Function.
2. Recursive invocation
Since cache_getImp returns nil, we continue through the for loop to the following code:
- Continue to
getMethodNoSuper_nolock
Function lookup method, but at this pointcurClass
Has becomesuperClass
. - If it is not found, the search will continue
superClass
untilNSObject
The parent classnil
, at this momentimp = forward_imp
willimp
To return.
Imp = forward_IMP, and what happens when imp is returned is explored in the next article.
Click a like to support it!! 😄 😄 😄