preface

In the previous chapter, we learned that a cache is used to cache methods; After calling the method:

  • cacheThe bottom layer is going to callinsertMethod to create a containerbucket_tAnd by occupyingoccupiedTo calculate the opening capacitycapacityWe need to be careful hereoccupied+2Is greater thanThree quarters ofCapacity needs to be expanded tocapacity*2;
  • Fetch the createdbucketsAnd then calculatemask(equal tocapacity-1) through mask and sel and then call the hash functioncache_hashGet the start position of the storage method;
  • To get thebeginAfter that, one will be calleddo-whileLoop to determine whether the calculated hash table index has a value, to prevent hash conflicts, if the table is occupied, then hash againcache_nextFinally, the method is stored in a hash table.

supplement

bucket_ttype

In the previous section we explored bucket_t data in cache_t in the following way:

Buckets ()[1] So buckets() is an array? Obviously, if we take it out, it’s not going to be an array of subscripts, so let’s look at the underlying structure of bucket_t,

It is a structure obviously, and we also use buckets()[index] as a structure.

  • First of all,buckets()Is equal tobucket_t *It’s a structure pointer;
  • [index]It actually meansFetch memory translation;buckets()It’s a struct pointer, so I’m going to shift this pointer by one, which is essentially the same thing asbuckets()+index;

###_bucketsAndMaybeMask ###_bucketsAndMaybeMask

So let’s evaluate this value by p over x

So we can say, well, actually _bucketsAndMaybeMaskinsidevalueThe values are the correspondingbucketsYou know thisbucketsFirst address so you can get the restbucketThe advantage of this is thatcache_tIt’s more structured, doesn’t take up a lot of memory, and users want to know something elsebucket, just according to the first address, and then offset the corresponding unit.

### the significance of 3/4 expansion

  • Good for space utilization
  • Effectively avoid hash conflicts

CacheCache method closed loop flow

Knowing that the cache contains an insert method for caching methods, who invoked the insert method? What is the process of cache writing and reading?

Insert (cache); insert (cache); insert (cache);

cachetheinsertMethod stack analysis

We set a breakpoint in the cache_t:: INSERT method:

And then run, passLLDBinstructionbtLook at the call stack, or of course look directly at the call flow on the left:

Cache ::insert = log_and_fill_cache = log_and_fill_cache = log_and_fill_cache

Then follow log_and_fill_cache to find that it is called in lookUpImpOrForward

As you can see from the comments at the top of the objc_cache.mm file, it’s important to take a look at objc_msgSend before exploring.

RuntimeTo explore the

Before we get to objc_msgSend we need to know the runtime runtime in iOS;

Compile time

In daily development, we basically write projects in higher-level languages, such as OC or Swift in iOS, but for machines, they cannot recognize high-level languages, so at this time there is a compiler, and the compiler compiles high-level languages. The process of translating it into an underlying language that the machine can recognize is called compile time

  • Compiler work: lexical analysis, syntax analysis, etc., also known as compiler type checking (also known as static type checking, so code is scanned as text before it is loaded into memory and run).

Runtime

runtimedefine

With compile time, you have run-time, which is when the code is loaded into memory and running, and then you have type checking, which is called runtime type checking, which means there are dynamic operation types in there; Just like in iOS, because of the runtime, because it’s loaded into memory at runtime, we can implement a lot of the stuff we want in between, dynamically adding properties, adding methods, etc.

Apple’s officialruntimeThe document

  • modernlegacyTwo versions, we’re using a correspondence right nowObjective - 2.0 cProgrammatic interfacemodernCurrent version; Mainly applicable toiPhoneProcedures andMac OS X v10.5And later in the system64A program.
runtimeAdjust the way

  • Objective-C CodeThat is our OC code layer, the source code;
  • FrameWork&SerivceFramework service layer;
  • Runtime ApiThat’s some of oursobjc_xxx.class_xxxAnd so on;
  • CompilerCompile layer;
  • Runtime System LibrarySome low-level libraries for runtime.
  1. Directly throughOCCode invocation: for example, calling object methods[person sayNB];
  2. throughNSObjectInterface: such as callisKindOfClassMethods;
  3. throughRuntime Api: e.g.class_getInstanceSizeEtc.

Let’s verify this with code:

Declare two classes, LhkhPerson inherited from NSObject and LhkhTeacher inherited from LhkhPerson

OC method call

Clang-rewrite-objc main.m -o main. CPP generates the main. CPP file.

We can see that there is an interpretation of the upper OC code after compilation, and that it is all sent through the objc_msgSend message.

supplement

We know that if we declare a method in a class and we don’t implement it, the object calling the method will get an error, but if we declare and implement the method in our parent class, the object calling the method does not get an error, and we can still call the method in the parent class. Why is that?

When we search for objc_msgSend in the main. CPP file we find:

So what does objc_msgSendSuper do? Let’s call this method directly, but we don’t know what parameters are required. Let’s look at objC source code

There are two arguments objc_super * and SEL op; Op, easy to understand, is SEL, which is the name of our method, so what is objc_super star?

So there are only two arguments in this structure, receiver and super_class. Receiver is our message receiver, and super_class is the parent of the message receiver. /* super_class is the first class to search */; if not, the method will continue to search according to the inheritance chain.

The OC code calls the method when calling the Runtime summary:

  • Call method = message send:objc_msgSend(Message receiver, message body (SEL + argument));
  • If the class method is called, then inobjc_msgSend(Message receiver, message body (SEL + argument)) method is converted to(id)objc_getClass("xxx")(XXX is the class name), or if it is an object method(id)xxx(XXX is the object name);
  • The name of the called method passes underneathsel_registerNameMethod is registered, and if there are parameters in the method called, then there are parameters in the message body;
  • The invoked method looks up the method implementation according to the inheritance chain.
NSObject interface

Direct use ofNSObjectInterface methods providedperformSelectorYou can turn it up directlyobjc_msgSendMessage sent.

Runtime Api

throughRuntime ApiDirect call. Pay attention to useRuntime ApiYou need to import the header file first#import <objc/message.h>Second, compilation will report an error, this time need to set:

objc_msgSendTo explore the

Use breakpoints to see where objc_msgSend is located:

We add a breakpoint at the call method, then check display assembly with Debug Workflow, and enter assembly description by holding down Control and clicking Console Step into

You can see that the source code is also in Libobjc (see the objc source link and how to configure it in the previous section);

By searching for objc_msgSend directly, we will find that there are too many. From the previous we know that objc_msgSend is written in assembly, so we will find the assembly file (.s file) directly.

Notice these macro definitions first

Here’s a look at the assembly code:

  • The first step is to enterobjc_msgSendMethod;
  • Compare the currentreceiverThe address and0Compare and decide whether you support it or notTAGGED_POINTERSType, viewSUPPORT_TAGGED_POINTERSMacro definitions we know are supported on 64-bit systems:
    • If it is less than or equal to 0, then it will go LNilOrTagged;
    • If it’s zero, then it’s LReturnZero, it just returns; There is no point in sending a message to an empty object;
  • If none of the above is met, thenx0The address in the register is read out top13Actually, this sidex0What’s stored isreceiver, i.eisaThe address (isaThe cache is in the class, so the cache is in the classisaTo get class information);
  • By macro definitionGetClassFromIsa_p16And passp131.x0Three parameters to obtainclass, that is, willclassDeposit top16;
  • And finally by gettingisalogoLGetIsaDone, the callCacheLookupAnd passes three argumentsNORMAL._objc_msgSend.__objc_msgSend_uncached.

So there’s a couple of points here, how does GetClassFromIsa_p16 get the class stored in P16, let’s see

GetClassFromIsa_p16

ExtractISA

  • In the first place to judgeSUPPORT_INDEXED_ISASearch to find this macro is mainly a judgmentarmv7k or arm64_32), obviously our side isarm64_64So go straight down__LP64__;
  • needs_authPass it through1“So I just leftExtractISA p16, \src, \auth_address;
  • ExtractISAIt’s also a macro definition,and    $0, $1, #ISA_MASKAccording to aboveGetClassFromIsa_p16The argument passed by the call to, $0 = p16.\src = $1\ SRC stands for ISA$0 = $1 & ISA_MASAnd we already know thatisawithonmaskAnd what you get isclassSo this side is going to beclassendures$0, that is,p16.

conclusion

Objc_msgSend (‘ isa ‘, ‘class’, ‘CacheLookup’);