• OC – objc_msgSend bottomWe exploredcache_tMethod cache insert.
  • OC bottom – the runtimeWe took a look at itcacheThe lookup process for the cache

Method search is divided into fast search and slow search, today we first explore the slow search method, to see how Apple engineers design, and the advantages of this design.

Slow search process

When we explored earlier, we saw that the method call is actually calling objc_msgSend to send the message, and then we saw through assembly code that objc_msgSend can mechanically determine whether the sending object is valid, whether the message is valid, and so on.

2. By following the process with the above assembly code, the following flowchart can be obtained:

3. One detail I didn’t notice when exploring earlier is the comment in the objc-cache.m file:

4. Apple clearly explains the main process of cache lookup and cache addition when objc_msgSend is triggered. So let’s follow the main process to look at the process of slow lookup, because we have analyzed the general cache lookup process above, and we only see the return of null value when we can’t find it, not the process of assigning value after finding it. Therefore, after re-reading, we find that another detail __objc_msgSend_uncached is ignored. We passed four arguments when we called CacheLookup, but we didn’t look at the fourth argument in detail, and it was the fourth argument that was the focus of the search process that was ignored, so let’s go back and look at this function again.

We can see that __objc_msgLookup_uncached only calls a MethodTableLookup function and returns it directly. We can see that __objc_msgLookup_uncached only calls a MethodTableLookup function and returns it directly.

In this function, we first save the class (the sender) to the X2 register, then assign the x3 register to 3, then jump to the _lookUpImpOrForward function, after the function is finished, we read IMP from the X0 register and save IMP to the X17 register. Look at the implementation of lookUpImpOrForward. As we have seen from our previous analysis, we searched for lookUpImpOrForward directly in the objC source code, and then entered the implementation as follows:

7. This brings us to one of the most important images of ISA in iOS:

8. Combining the slow search process with the isa bitmap, we can finally get the following flowchart:

isInitialized

We introduced the underlying structure of a class earlier when we explored the underlying structure of a class, which consists of the following:

1. Let’s take a look at the isInitialized function. This function reads bits through ISA and then reads flags through bits.

#define RW_INITIALIZED (1<<29) #define RW_INITIALIZED (1<<29)

(1) 0010 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 The result is 0000 0000 0000 0000 0000 0000, the final result is 0, which returns false.

4. Then we can view the result by using the breakpoint mode. If it is alloc or new, it will be uninitialized.

checkIsKnownClass

(objc_duplicateClass, objc_initializeClassPair, objc_allocateClassPair) (objc_duplicateClass, objc_initializeClassPair, objc_allocateClassPair) (objc_duplicateClass, objc_initializeClassPair, objc_allocateClassPair)

2, isKnownClass is called, the system also has the corresponding comment, In isa->bits->witness, check whether the class is legally registered by objc_duplicateClass, objc_initializeClassPair, and objc_allocateClassPair during compilation.

realizeAndInitializeIfNeeded_locked

1. This function is implemented as follows:

This function does two things. First, it checks if the given class is implemented, and if not, it implements it. Second, whether the given class is initialized, if not.

3, we first look at the first decision whether to implement the class decision, this block calls isa->bits->flags and then carries on the and operation, the obtained flags and 1<<31, through the breakpoint can see the obtained values as follows:

The code in main.m is as follows:

Then the calculation results are as follows:

3580144:0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0011 0110 1010 0000 1111 0000 1 << 31: 0010 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 bear fruit: 0000 0000 0000 0000 0000 0000 0000 0000 0000Copy the code

If (slowPath (! cls->isRealized())) {… }, so it will go to the if branch, and then implement the class.

4, and then we see second determine whether to the initialization of classes, this call the isa – > bits – > flags then and operations, will get to the flags and 1 < < 29 through breakpoints can see the access to the value is as follows:

Then the calculation results are as follows:

3580144:0000 0000 0000 0000 0000 0011 0110 1010 0000 1111 0000 1 << 29: 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000Copy the code

The initialize value is false, and the initialize value is true. So the initialize value is true, and the class is initialized.

getMethodNoSuper_nolock

Select * from ‘IMP’ where ‘IMP’ = ‘IMP’ and ‘IMP’ = ‘IMP’;

The search_method_LIST_inline function is an inline function.

3, outside processing logic is carried out in accordance with the method of length displacement reading method, we focus on the search_method_list_inline function implementation:

4, the above function is mainly to search method good reading list or unordered list of methods, the realization of the key we continue to see findMethodInSortedMethodList function:

5, this is the last node of the method search, if not found here, then return nil and start to forward the process.

findMethodInSortedMethodList

1, in this method, Apple can be said to use binary search to the extreme ah, let’s see the specific practices and code:

2, first, in the outer layer of the definition of a list of methods to calculate the maximum value, this graph, I feel more intuitive:

3. Let’s read the algorithm of this piece, assuming that the count obtained by this is 14, so the operation process is as follows:

count = 14; base = 0; probe = 0; Probe = base + (count>>1); Probe = 0 + 14>>1 probe = 0 + 1110>>1 probe = 0 + 0110 probe = 0 + 6 probe = 6 base = 6 + 1 = 7 Second cycle probe = base + (count>>1); Probe = 7 + 14>>1>>1 probe = 7 + 1110>>1>>1 probe = 7 + 0110>>1 probe = 7 + 0010 probe = 7 + 2 probe = 9 base = 10 The third cycle  probe = base + (count>>1); probe = 10 + 14>>1>>1>>1 probe = 10 + 1110>>1>>1>>1 probe = 10 + 0110>>1>>1 probe = 10 + 0010>>1 probe = 10 + 0001>>1 probe = 10 + 1 probe = 11 base = 12Copy the code

(curClass = curClass->getSuperclass()) == nil); (curClass = curClass->getSuperclass() == nil); (curClass = curClass->getSuperclass() == nil);

conclusion

Method calls to the message is sent to the method to find the is an overall process, only the apple made a lot of details in the process optimization and fault tolerance, so when we read the source code looks messy, but when you go through the process again, and then summed up the main line, you will find that is very simple, in addition to the main line is fault-tolerant processing and other details about the process optimization.