preface

The first two main articles explored the underlying flow of calling an object’s alloc, locating it to the core method _class_createInstanceFromZone, which does three main things:

  • Calculate the size of memory space to be opened up;
  • Apply for memory;
  • Returns the address pointer to the classisaAssociated;

Originally, it mainly explored the process of associating part 3 classes with ISA.

NONPOINTER_ISA

Before exploring the process, take a look at NONPOINTER_ISA. Current Apple devices use 64 cpus and have addressing power of 0-2 to the power of 64. Pointers to 64-bit programs take up 8 bytes of memory, but they don’t really need that much memory address. Apple optimizes it to store pointer address (rawisa) processing, and stores some other information, making full use of the 8 bytes of memory. This is NONPOINTER_ISA, and the bottom ISA union of isa_t. Here is the collated ISA_T, since the code runs on an x86 64-bit environment, as an example:

#   define ISA_MASK        0x00007ffffffffff8ULL // To access the pointer value stored by Shiftcls
#   define ISA_MAGIC_MASK  0x001f800000000001ULL
#   define ISA_MAGIC_VALUE 0x001d800000000001ULL
#   define ISA_HAS_CXX_DTOR_BIT 1

// This is a consortium
union isa_t {
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { }

    uintptr_t bits;

private:
  
    Class cls;

public:
    //arm 64 fields are similar
    // The bitfield is used for storage
    struct {
      //nonpointer 0 ISA pure ISA pointer and 1 ISA nonpointer_isa pointer
      uintptr_t nonpointer        : 1;
      // Whether the associated object has been set
      uintptr_t has_assoc         : 1;     
      // whether there is a C++ destructor (.cxx_destruct)
      uintptr_t has_cxx_dtor      : 1;   
      // Store the memory address information of Class and meta-class objects
      uintptr_t shiftcls          : 44;
      // Used to tell if an object is not initialized during debugging
      uintptr_t magic             : 6;  
      // If there is a weak reference to point to
      uintptr_t weakly_referenced : 1;   
      // Not in use
      uintptr_t unused            : 1;  
      // Whether the flag has a sitetable structure for storing reference counts
      uintptr_t has_sidetable_rc  : 1;   
      // Mark the reference count of the object (first stored in this field, when the upper limit is reached, stored in the corresponding reference count table)
      uintptr_t extra_rc          : 8 
    };
}

Copy the code

Union and bitfield

What is a union

A union is also called a synthete and, like a structure, is a type used to construct data. The union is defined as follows

Union name {member list};Copy the code

The difference between a structure and a union is that the members of a structure occupy different memory and have no effect on each other. While all members of the shared body occupy the same segment of memory, the memory occupied by the longest member is equal to the memory occupied by the longest member. Modifying one member affects all other members, and assigning a value to one member overwrites the value of the other members.

What is a bitfield

Some data can be stored in one or more bits rather than a complete byte. For example, the nonpointer field values 0 and 1 can be stored in only one bit. Therefore, C language provides a data structure called bitfield. When defining a structure, you can limit the number of digits that a member variable can occupy by adding a number following “:”.

struct {
      uintptr_t nonpointer        : 1;
      uintptr_t has_assoc         : 1; . };Copy the code

Isa association process

1. Create a Person object as you did with alloc(above)

int main(int argc, const char * argv[]) {
    Person *person1 = [Person alloc];
    return 0;
}
Copy the code

2. Set the breakpoint to _class_createInstanceFromZone and call obj->initInstanceIsa(CLS, hasCxxDtor)

    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);
        
    }
Copy the code

3. After entering initInstanceIsa, initIsa(CLS, true, hasCxxDtor) will be called. InitIsa mainly creates an ISA_T structure object, and then assigns the value to the ISA field of the object structure to complete the association.

inline void 
objc_object: :initIsa(Class cls, bool nonpointer, UNUSED_WITHOUT_INDEXED_ISA_AND_DTOR_BIT bool hasCxxDtor){ ASSERT(! isTaggedPointer()); isa_t newisa(0);

    if(! nonpointer) { newisa.setClass(cls,this);
    } else{ ASSERT(! DisableNonpointerIsa); ASSERT(! cls->instancesRequireRawIsa()); #if SUPPORT_INDEXED_ISA
        ASSERT(cls->classArrayIndex() > 0);
        newisa.bits = ISA_INDEX_MAGIC_VALUE;
        // isa.magic is part of ISA_MAGIC_VALUE
        // isa.nonpointer is part of ISA_MAGIC_VALUE
        newisa.has_cxx_dtor = hasCxxDtor;
        newisa.indexcls = (uintptr_t)cls->classArrayIndex();
#else
        newisa.bits = ISA_MAGIC_VALUE;
        // isa.magic is part of ISA_MAGIC_VALUE
        // isa.nonpointer is part of ISA_MAGIC_VALUE
#   if ISA_HAS_CXX_DTOR_BIT
        newisa.has_cxx_dtor = hasCxxDtor;
#   endif
        newisa.setClass(cls, this);
#endif
        newisa.extra_rc = 1;
    }

    isa = newisa;
}
Copy the code

4. Take a look at the isa_t setClass method, which has a lot of IF macro judgment, but in this case the final implementation of the environment is Shiftcls = (uintptr_t)newCls >> 3, which assigns the pointer to the class address 3 bits right to Shiftcls.

inline void
isa_t: :setClass(Class newCls, UNUSED_WITHOUT_PTRAUTH objc_object *obj)
{
    // Match the conditional in isa.h.
#if __has_feature(ptrauth_calls) || TARGET_OS_SIMULATOR
    ...
#else // Nonpointer isa, no ptrauth
    shiftcls = (uintptr_t)newCls >> 3;
#endif
}
Copy the code

Why did the address move 3 bits to the right? With this problem in mind, let’s look at how to get the address of a pointer to a class.

5. If there is setClass, there must be getClass, as follows, ISA_MASK is the macro constant defined above.

inline Class
isa_t: :getClass(MAYBE_UNUSED_AUTHENTICATED_PARAM bool authenticated){#if SUPPORT_INDEXED_ISA
    ...
#   else
    clsbits &= ISA_MASK;
#   endif

    return (Class)clsbits;
#endif
}

Copy the code

Person1; person1; person1; person1; person1; person1; person1

We’re going to be at this pointisaandISA_MASKIt’s a little bit more intuitive to do the sums in binary

As you can see,& ISA_MASKYou’re essentially erasing the values of the other digits, because at4.whensetClassMethod to place the addressWe moved 3 places to the rightThat’s 3 places lower0Just catch up onThree to the rightAnd because it is 8-byte aligned the lower three digits are 0, there is no data loss.

conclusion

At this point, through debugging objC source code to complete the exploration of alloc process. As we explore the process, we discover more things worth exploring, and the more we explore, the less we feel we know, and the lower level of exploration is just beginning.