The introduction

We’ve looked at the alloc process, the fundamental factors that affect the size of an object’s memory, and so on, but have we ever thought about what an object is? So let’s explore the nature of objects.

Probe into the essence of structure

Let’s start with the code:We created an inheritance withNSObjecttheLhkhPersoObject, but don’t you think thisLhkhPersonIs it abstract? itWhat is the? How to recognize it? After the previous study, we have mastered several ways to learn the bottom layer, we can explore the bottom layer through assembly, LLDB debugging, bottom source debugging and other means, so next we will explore the nature of the object through a more fun thing, namelyClang.

The Clang know

Clang and its Documentation Clang documentation

Clang can be understood as a llVM-based C, C++, objective-C language lightweight editor, is written by Apple; Clang supports its normal lambda expressions, simplified handling of return types, and better handling of constexpr keywords. We know OC language is a high-level language, we tend to see how its underlying implementation, the Clang of the role is to our OC language code files compiled into the underlying language, for instance can be OC. M documents compiled into. CPP file, facilitate better we see the underlying implementation, then we will come to explore.

Clang command

Clang-rewrite-objc main.m -o main. CPP // If you use this command, you can use it directly. Use the clang-x objective-c-rewrite-objc-isysroot command /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk main.m // When we install xcode, we already have the xcrun command installed. The xcrun command is wrapped in clang -rewrit-objc main.m -o main-arm64. CPP // compile xcrun - SDK iphoneos clang -arch arm64 - rewrit-objc main.m -o main- arm64.cppCopy the code

CPP: LhkhPerson: LhkhPerson: LhkhPerson: LhkhPerson: LhkhPerson: LhkhPerson: LhkhPerson: LhkhPerson: LhkhPerson: LhkhPerson: LhkhPerson: LhkhPerson

After directly locating the source code, we found:

  • LhkhPersonIt’s actually aThe structure of the body(struct LhkhPerson_IMPL);
struct NSObject_IMPL NSObject_IVARS; // Isa inherits from NSObject. NSString *_lhkhName; // Our custom propertiesCopy the code
  • NSObjectSubstance is also aThe structure of the bodyThere is only one member variable insideClass isa;
Class we already know from our previous exploration that it's an objc_class star, which is a pointer to a structure so let's verify that here and let's see, by searching for objc_class typedef struct objc_class star Class; struct objc_object { Class _Nonnull isa __attribute__ ((deprecated)); }; Here, if you're a little bit more observable, you might notice that id, SEL, is also a pointer to a structure, which explains why we don't have to add "*" to the type defined by id, typedef struct objc_object *id; typedef struct objc_selector *SEL;Copy the code
  • typedef struct objc_object LhkhPerson.typedef struct objc_object NSObject, both of themobjc_objectThis type; I need to be careful hereOC layerAll our objects are inherited fromNSObject, but inOC belowObject isobjc_object.

Extensions to object member variables and properties

Let’s add a member variable. Let’s look at the c++ file

  1. The underlying property declaration via @property generates the _lhkhName variable and its corresponding getter and setter methods, while the member variable inside the braces generates only its corresponding variable. And that proved itOCThe dot syntax, through@propertyThe declared property layer automatically adds tape to it_Variables andgetterandsetterMethod, so we can add by object.Member variables in curly braces generate only the declared variable and do not have itgetterandsetterMethods.
  2. The bottom of the(*(NSString **)((char *)self + OBJC_IVAR_$_LhkhPerson$_lhkhName)) This is to get to the currentlhkhNameBy obtaining the initial address of the current object plus the offset of the attribute to get the address of the attribute, and then get the value stored in the address, and finally convert toNSStringType.

Summary of object Essence

By exploring the above c++ files, we can determine that the object is actually a structure whose first member variable is inherited from the parent isa class.

So we know that an object is essentially a structure and its first member variable is isa inherited from its parent class, so what is ISA?

isaTo explore the

Before we explore ISA, we need to understand some basics. We know that the memory of a structure is the sum of the size of its internal elements and is opened up according to the principle of structure, have we ever thought that in some cases this will be a waste of memory, do you think Apple is so meticulous that he does not know? So to avoid these situations, Apple came up with bitfields and unions, so let’s take a look at what bitfields and unions are:

A domain

Let’s look at the example above. LhkhCar1 is a structure. We know that LhkhCar1 has a memory size of 4 bytes (BOOL has a memory size of 1 byte). In fact, it is the bit field inside the problem…

Note that :1 in LhkhCar2 refers to the bit field, indicating that the current element only occupies 1 bit. 1byte = 8 bitCopy the code

So let’s analyze that

0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000 But there is not half a byte, so one byte is enough, which results in LhkhCar1 wasting three times the memory; Let's look at the bit-field structure LhkhCar2, which takes 1 byte. Why is that? Let's think about it, can a car go backwards at the same time as it's going forward? Obviously it can't, so the bit field is designed to make these variables mutually exclusive, so you can have it, you can't have it, so it saves memory.Copy the code

A consortium

Let’s look at one more example structurestructAnd a consortiumunion We’re throughlldbDebug discovery structuret1The variables in it all end up with the values that we assign, and we find the uniont2The value appears at the beginning of the declaration, which can be seen as dirty data allocated by the system. Then, as we step by step, we find that the value of the previous variable is lost after each variable is assigned.

From the above example we can summarize structures and unions

  • The structure of the bodystructAll of the variables in it are PIcoexistenceRelationship, the advantage is’ tolerance big ‘, comprehensive; The disadvantage is that the memory space allocation is extensive, whether you use all allocation or not;
  • A consortiumunionAll of the variables in it are PIThe mutexRelationship, the advantage is the use of memory fine flexible, save space; The downside is not being ‘inclusive’ enough.
Note: The memory size of the union depends on the size of the largest variable inside it. For example, the largest variable in T2 above is double which takes 8 bytes, so the final size of the union is 8Copy the code

isaThe internal structure

So once we understand the union and we’ll go back and review when we explore alloc, we’re going to end up calling three methodsinstanceSize.calloc.initInstanceIsaWe’ve already seen that. SoinitInstanceIsaWhat does the process look like, let’s go through it againobjcThe source code we enter through the alloc processinitInstanceIsaMethod,And then it goes intoinitIsaMethod,

Note: nonpointer isa non-pointer source. We know that isa takes 8 bytes, which is 64 bits. We're going to have a bunch of ISA classes in our project, so how are we going to save memory, so we're going to have nonpointers, not just Pointers in ISA, Internal structures can also include whether it is being freed, reference count, weak, association class, destructor, etc. You can see that the initInstanceIsa method is directly passed true, which isa non-pointer source. This opens the way for us to explore what is stored in isaCopy the code

We can get it from the source codeisaThe type ofisa_tSo we go intoisa_t We found thatisa_tIt’s a union with two variables in itclsbits, and a macro definitionISA_BITFIELD

This macro corresponds to the arm_64 iOS side and __x86_64 macOS side, using the same bit field we explored above.

Noun explanation:

Nonpointer: indicates whether the ISA pointer is optimized. 0 indicates a pure pointer. 1 indicates that the ISA contains more than the address of the class object. Shiftcls: stores the value of class Pointers. With pointer optimization enabled, there are 33 bits for storing class Pointers in arm64 and 44 bits for storing class Pointers in X86_64. Referenced: An object that is used by the debugger to determine whether the current object is a real object or an uninitialized space. Weakly_referenced: A weak variable that refers to whether the object is or has ever been referred to an ARC, an object without a weak reference can be freed deally_mark faster: Indicates whether the object is releasing has_SIDETABLE_RC: When the object reference count is greater than 10, you need to borrow this variable to store the carry hextra_RC: For example, if the object's reference count is 10, the extra_RC is 9. If the object is greater than 10, you need to use has_sidetable_rc aboveCopy the code

We can take a look at the distribution of arm_64 and __x86_64 64-bit storage

isaAssociation class

Shiftcls: ShiftCls: ShiftCls: ShiftCls: ShiftCls: ShiftCls

Use LLDB x/4gx to print the address information of the current object, and then p/t to print the 64 storage information of the whole ISA

Side of theISA_MASKIs a mask. Its main function is to filter out other information through the mask and return only the class information, i.eshiftclsThe content of the.

Let’s go ahead and go through the break point into the initIsa method

Further down,You can findisaThe core code associated with a class is insetClassThis method inside, goes intosetClassIn the

Why hereshiftcls = (uintptr_t)newCls >> 3

Now that we know the 64-bit distribution, let’s do it the other way aroundKnow themacOSEnd 64 bit distribution in order to getshiftclsThe information firstMoves to the rightWe get rid of the first three bits of information, and then we know the originalshiftclsWe have 17 bits, plus the 3 bits we moved to the rightThe left 20, discard the last 17 bits of information, and then finallyMoves to the right of 17That reductionshiftclsTo get the information of the corresponding class, look at the print above, the result is exactly the same, this is alsoisaKey steps associated with a class.

initandnewTo explore the

Search through the source codeinit The objectinitAnd then it goes into_objc_rootInit, directly returnobjc

Take a look atnewSearch through source codenewIn fact, the class call to new is actually translated into a call in the source codecallAllocandinit