The alloc process was analyzed in the last article, where the initialization of ISA was not explored. This article will continue to explore the initialization and pointing analysis of ISA.

Environment: Xcode 11.5

Source: objc4-781

In alloc, the calloc method allocates a certain amount of memory to store objects. What objects do we store?

static ALWAYS_INLINE id _class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone, int construct_flags = OBJECT_CONSTRUCT_NONE, bool cxxConstruct = true, size_t *outAllocatedSize = nil) { ... // Print obj and return 0x0000000101134ff0 if (! zone && fast) { obj->initInstanceIsa(cls, hasCxxDtor); } else { // Use raw pointer isa on the assumption that they might be // doing something weird with the zone or RR. obj->initIsa(cls); } // print obj and return <ZHYObject: 0x101134ff0>...Copy the code

As you can see from the above print, it is only after initInstanceIsa that the system associates a particular memory space with a familiar class. We also know that an instance’s ISA points to its class, and a class’s ISA points to its metaclass. So how is it implemented inside ISA? What does the structure look like? First take a look at the internal implementation of initInstanceIsa.

inline void objc_object::initInstanceIsa(Class cls, bool hasCxxDtor) { assert(! cls->instancesRequireRawIsa()); assert(hasCxxDtor == cls->hasCxxDtor()); initIsa(cls, true, hasCxxDtor); } inline void objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor) { ... isa_t newisa(0); . newisa.bits = ISA_MAGIC_VALUE; // isa.magic is part of ISA_MAGIC_VALUE // isa.nonpointer is part of ISA_MAGIC_VALUE newisa.has_cxx_dtor = hasCxxDtor; newisa.shiftcls = (uintptr_t)cls >> 3; // This write must be performed in a single store in some cases // (for example when realizing a class because other threads // may simultaneously try to use the class). // fixme use atomics here to guarantee single-store and to // guarantee memory order w.r.t. the class index table // ... but not too atomic because we don't want to hurt instantiation isa = newisa; }}Copy the code

The initIsa method is eventually executed. By reading the source code, we can see that ISA_T is the core part that we need to explore.

union isa_t {
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { }

    Class cls;
    uintptr_t bits;
#if defined(ISA_BITFIELD)
    struct {
        ISA_BITFIELD;  // defined in isa.h
    };
#endif
};
Copy the code

Isa_t isa union type that also defines a bitfield, ISA_BITFIELD. You can read structs, unions, and bit-fields for unions, structures, and bit-fields. View the internal structure of this bitfield:

Only look at the definition under x86 # elif __x86_64__ # define ISA_MASK 0x00007ffffffffff8ULL / / binary representation is: 0 b0000000000000000011111111111111111111111111111111111111111111000 # define ISA_MAGIC_MASK 0 x001f800000000001ull / / binary representation is: 0 b0000000000011111100000000000000000000000000000000000000000000001 # define ISA_MAGIC_VALUE 0 x001d800000000001ull / / binary representation is: 0 b0000000000011101100000000000000000000000000000000000000000000001 # define ISA_BITFIELD uintptr_t nonpointer : 1; // uintptr_t has_assoc: 1; // uintptr_t has_cxx_dtor: 1; Uintptr_t shiftcls: 44; // Uintptr_t shiftcls: 44; // Uintptr_t shiftcls: 44; /*MACH_VM_MAX_ADDRESS 0x7ffffFE00000 Uintptr_t Weakly_referenced: 1; uintptr_t weakly_referenced: 1; // If the object is pointing to or used to point to an ARC weak variable, an object without a weak reference can release the uintptr_t deallocating: 1 faster; // Flag whether the object is releasing memory uintptr_t has_sidetable_rc: 1; // If the object reference technique is greater than 10, the variable needs to be borrowed to store the extra_rc in uintptr_t: 8; //axisPointer ... #endifCopy the code

You can see that the size of isa_t is 8 bytes, or 64 bits. Going back to the initIsa method above, the main code for ISA initialization is as follows:

isa_t newisa(0); // Initialize a union of isa_t newISa.bits = ISA_MAGIC_VALUE; newisa.has_cxx_dtor = hasCxxDtor; newisa.shiftcls = (uintptr_t)cls >> 3; // Store the corresponding class informationCopy the code

Next we verify with LLDB that the classes in ISA are stored in the same location as we found by reading the source code.

ZHYObject *object1 = [[ZHYObject alloc]init];
Copy the code

It was finally removed by a series of displacement operationsisa_t4-47. withZHYObjectClass memory comparison found exactly the same.

In addition to that, in the previousisa_tThere is also a bit-field definition calledISA_MASKThe macro function is as a mask that we can pass directly withISA_MASKTo conduct a&Operation, directly to the corresponding values, andZHYObjectThe memory of the classes is the same.

Do not know everybody has this question, why representclsPointer toisa_tThe first in the union4Bit start store? That means going through the maskISA_MASKTo get theshiftclsThe last three will always be0This is because the memory we open up is based on8Byte alignment, which means the memory address can only be8The multiples of, that is, the last three are000.

And then there’s this famous picture

This diagram represents instances of classes in ios, class objects, and a relationship between metaclasses and their superclasses. And oursZHYObjectFor example, let’s verify.

X /4gx prints out the memory structure

struct objc_class : objc_object { // Class ISA; Class superclass; .Copy the code

In the figure above, we started from oneZHYObjectThe instance starts throughisaLook up step by step, in orderThe instance->class->The metaclass->A metaclass, found through memory comparison:

  • The instancetheisaPoints to theZHYObject metaclass
  • ZHYObject metaclasstheisaPoints to theA metaclass
  • A metaclasstheisaPoints to theA metaclass

Also, since the second 8 bytes in the obCJ_class structure represent its parent class, we can also verify that the reference to the parent class is correct, namely:

  • ZHYObject classThe parent classNSObject
  • ZHYObject metaclassThe parent classA metaclass
  • A metaclassThe parent classNSObject
  • NSObjectThe parent classnil

That’s the initialization and pointing analysis of ISA.