In OC bottom-Runtime, we learned that the essence of method calls is to send messages. In this article, we explore the underlying principles of objc_msgSend and look at the message sending mechanism of the system after calling methods.

objc_msgSend

Search for objc_msgSend in the objc source code.

Dummy – libnary -mac-i385.c – object – msgsend – object – object – object – object – object – object – object – object – object – object – object – object – object Generally speaking. S files are assembly language code, and we are currently running in the virtual machine project, so we directly choose the architecture is i386. S files for analysis.

3. We can see the entire objc_msgSend assembly code as follows:

4. The general flow of the above assembly code is shown in the figure below:

5. The above is a process of objc_msgSend that we can see in objC source code. The reason why Apple uses assembly code directly in this area should be for efficiency reasons, because assembly is the closest kind of coding to machine language.

Cache supplement

1. After the introduction of objc_msgSend, we will continue to introduce the expansion of cache. In the previous section, we have seen a process of cache, but we did not analyze the storage procedure of cache in detail.

Void cache_t::insert(SEL SEL, IMP IMP, ID Receiver) void cache_t::insert(SEL SEL, IMP IMP, ID Receiver) First, let’s look at the implementation of this function:

3. After entering the function, the lock operation is carried out to ensure the security of read and write operations. Then, a judgment is made to determine whether the current message sender is going to initialize. The isConstantOptimizedCache() function is called, Inline bool isConstantOptimizedCache(bool uintPtr_t empty_addr = 0) const {return false; } returns false directly, so skip this and move on, focusing on the sequence of processing of mechanical energy starting at line 850.

The final function is capacity(); the final function is capacity(); the final function is capacity(); the final function is capacity();

unsigned cache_t::capacity() const
{
    return mask() ? mask()+1 : 0; 
}
Copy the code

5, mask() is called, this mask() is mainly through a series of processing to read the capacity and then -1 to get a value, and then judge whether the value is greater than 0, if greater than 0 + operation, if not directly return 0.

If capacity is 0, assign 4 to it. If capacity is 0, assign 4 to it. If capacity is 0, assign 4 to it. Void cache_t::reallocate(mask_t oldCapacity, mask_t newCapacity, bool freeOld) void cache_t::reallocate(mask_t oldCapacity, mask_t newCapacity, bool freeOld)

This function is also used to set the cache member data according to the new buckets() pointer. Then let’s look at the implementation of setBucketsAndMask:

8. Since our project is running under the ARM64 architecture, we will look directly at the LOGIC of the ARM architecture. Then we go back to insert and look at the other conditional branches. The important data here is 3/4 and 7/8. This is a coefficient. We already know that the cache is a hash table. And then the other one is if the cache is full, then go back to the reallocate function and open up new memory and free up old memory.

Mask_t m = capacity – 1; This is looking for storage, but looking forward, and then doing a walk, looking for empty memory that can be inserted, and then caching the method into the bucket when it is found.

10. The exploration of the basic process of cache has been completed. We will add new discoveries in the future.