We’ve already seen how objects are created, but there’s a small detail here. Obj = (id)calloc(1, size) is executed in the _class_createInstanceFromZone method. Obj ->initInstanceIsa(CLS, hasCxxDtor); Obj is the MYPerson object that we created, because we initialized an ISA and associated it with the class, and we know that we can find the class from the object’s ISA, so today we’re going to look at isa

One: What is ISA

Isas come in two types: a pure pointer (Class type) and a nonpointer (nonpointer) and some other information to optimize memory. We all know that an ISA is 8 bytes and 64 bits, so each of these bits contains some information about the class for memory optimization.

1. Definition of ISA

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

2. Definition of Class

Class is actually a pointer to objc_class

struct objc_class : objc_object {
    // Class ISA;
    Class superclass;
    cache_t cache;             // formerly cache pointer and vtable
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags

    class_rw_t *data() { 
        return bits.data();
    }
    .....此处省略
}    
Copy the code

3. The bits are defined

typedef unsigned long           uintptr_t;
Copy the code

From the above definition, isa is actually an ISA_T union with CLS and BITS members. What is the union? The union shares memory with its internal members, and the size of memory depends on which member occupies the most memory, either a structure pointer or an unsigned long, both of which are 8 bytes, so isa_t is 8 bytes.

Two: isa memory optimization

We start with the bit field ISA_BITFIELD in the ISA_T union

1. A domain

The following is the definition of bit-field in Baidu Encyclopedia

Information is generally accessed in bytes. In fact, sometimes it is not necessary to store one or more bytes of information; for example, “true” or “false” is represented by 0 or 1, but only one bit. When the computer is used in the field of process control, parameter detection or data communication, the control information usually only occupies one or several bits in a byte, and several bits of information are often put in a byte

2. ISA_BITFIELDdefine

# if __arm64__
# define ISA_MASK 0x0000000ffffffff8ULL
# define ISA_MAGIC_MASK 0x000003f000000001ULL
# define ISA_MAGIC_VALUE 0x000001a000000001ULL
# define ISA_BITFIELD \
      uintptr_t nonpointer        : 1;                                       \
      uintptr_t has_assoc         : 1;                                       \
      uintptr_t has_cxx_dtor      : 1;                                       \
      uintptr_t shiftcls          : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \
      uintptr_t magic             : 6;                                       \
      uintptr_t weakly_referenced : 1;                                       \
      uintptr_t deallocating      : 1;                                       \
      uintptr_t has_sidetable_rc  : 1;                                       \
      uintptr_t extra_rc          : 19
# define RC_ONE (1ULL<<45)
# define RC_HALF (1ULL<<18)

# elif __x86_64__
# define ISA_MASK 0x00007ffffffffff8ULL // ULL represents a hex suffix mask
# define ISA_MAGIC_MASK 0x001f800000000001ULL
# define ISA_MAGIC_VALUE 0x001d800000000001ULL
# define ISA_BITFIELD \
      uintptr_t nonpointer        : 1;                                         \
      uintptr_t has_assoc         : 1;                                         \
      uintptr_t has_cxx_dtor      : 1;                                         \
      uintptr_t shiftcls          : 44; /*MACH_VM_MAX_ADDRESS 0x7fffffe00000*/ \
      uintptr_t magic             : 6;                                         \
      uintptr_t weakly_referenced : 1;                                         \
      uintptr_t deallocating      : 1;                                         \
      uintptr_t has_sidetable_rc  : 1;                                         \
      uintptr_t extra_rc          : 8
# define RC_ONE (1ULL<<56)
# define RC_HALF (1ULL<<7)

# else
# error unknown architecture for packed isa
# endif
Copy the code

As we can see from the code, there are two architectures, ARM64 and X86_64, but the bitfield size is 8 bytes, 64 bits.

3. What does each stand for

  • Nonpointer: indicates whether pointer optimization is enabled for isa Pointers
  • 0: pure ISA pointer
  • 1: Not only class object address, ISA contains class information, object reference count, etc
  • Has_assoc: flag bit of the associated object. 0 does not exist and 1 exists
  • Has_cxx_dtor: does the object have a destructor for C++ or Objc? If it has a destructor, the destructor logic needs to be done. If not, the object can be freed faster
  • Shiftcls: Stores the value of the class pointer. With pointer optimization turned on, 33 bits are used to store class Pointers in the ARM64 architecture.
  • Magic: Used by the debugger to determine whether the current object is a real object or has no space to initialize
  • Weakly_referenced: Indicates whether an object is pointed to or has been pointed to an ARC weak variable. Objects without weak references can be released faster.
  • Deallocating: Indicates whether the object is freeing memory
  • ** has_SIDETABLE_rc :** When the object reference technique is greater than 10, this variable is borrowed to store carry
  • Extra_rc: When representing the referential count value of this object, it is actually the referential count value minus 1

As you can see from the above, a lot of information is stored in the bitfield, and the 8-byte 64-bit bits have been used to their fullest extent. Note that Shiftcls stores the value of the class pointer, which is also used to find the class to which it belongs

Three: ISA points to analysis

Class inherits from NSObject, so there must be ISA, and ISA must come first

Class stuClass = [STU1 Class] First, look at the internal implementation of the class method. We see that the object_getClass(self) method is called

- (Class)class {
    return object_getClass(self);
}
Copy the code

Object_getClass (self) internal implementation

Class object_getClass(id obj)
{
    if (obj) return obj->getIsa();
    else return Nil;
}
Copy the code

Internal implementation of getIsa

objc_object::getIsa() 
{
    if(! isTaggedPointer())return ISA();

    uintptr_t ptr = (uintptr_t)this;
    if (isExtTaggedPointer()) {
        uintptr_t slot = 
            (ptr >> _OBJC_TAG_EXT_SLOT_SHIFT) & _OBJC_TAG_EXT_SLOT_MASK;
        return objc_tag_ext_classes[slot];
    } else {
        uintptr_t slot = 
            (ptr >> _OBJC_TAG_SLOT_SHIFT) & _OBJC_TAG_SLOT_MASK;
        returnobjc_tag_classes[slot]; }}Copy the code

The key point here is ISA(), which takes a look at its internal implementation

objc_object::ISA() { assert(! isTaggedPointer());#if SUPPORT_INDEXED_ISA
    if (isa.nonpointer) {
        uintptr_t slot = isa.indexcls;
        return classForIndex((unsigned)slot);
    }
    return (Class)isa.bits;
#else
    return (Class)(isa.bits & ISA_MASK);
#endif
}
Copy the code

Return (Class)(ISa.bits & ISA_MASK) returns the value of the bitwise and operation to Class. ISA_MASK is the mask, which has been defined in the above bit field –> 0x0000000ffffffff8ULL

Let’s verify our conjecture by printing the memory structure of stu1, so 0x001d8001000014a9 represents ISA

(lldb) x/4xg stu1
0x1018131d0: 0x001d8001000014a9 0x0000000000000000
0x1018131e0: 0x72726f43534e5b2d 0x65546e6f69746365
(lldb)
Copy the code

At this point we print 0x001D8001000014a9 and there’s nothing there, right

(lldb) po 0x001d8001000014a9
8303516107936937
Copy the code

Let’s try this (ISa.bits & ISA_MASK), plus the mask.

(lldb) p/x 0x001d8001000014a9 & 0x0000000ffffffff8
(long) $7 = 0x00000001000014a8
(lldb) po 0x00000001000014a8
WYStudent
Copy the code

At this point, our conjecture has been verified and we have obtained the information of the class through ISA.

In the same way as above, the class isa points to the metaclass, the metaclass ISA points to the root metaclass, and the root metaclass ISA points to itself.