Surrounding information:

1. Structure

A structure is an important data type in C, which consists of a set of different data called members (or domains, or elements), each of which can have a different type. Structures are often used to represent data of different but related types.

2. Consortium

“Union” has some similarities with “structure”. But there are fundamental differences. In a structure, each member has its own memory space. The total length of a structure variable is greater than or equal to the sum of the member lengths. In union, the members share a memory space, and the length of a union variable is equal to the longest length of each member.

3, a domain

Some information does not need to occupy a complete byte, but only a few or a binary bit. For example, when storing a switch quantity, there are only 0 and 1 states, and a binary can be used. In order to save storage space, and make processing simple.

Documents:

1, objc source download opensource.apple.com/tarballs/ob…

Source code analysis:

1, Isa intrinsic union 2, ISA_BITFIELD 3, initializing the core method ISA intrinsic union When multiple data needs to share memory or multiple data at a time, can be used

struct objc_object { private: isa_t isa; public: // ISA() assumes this is NOT a tagged pointer object Class ISA(bool authenticated = false); // rawISA() assumes this is NOT a tagged pointer object or a non pointer ISA Class rawISA(); // getIsa() allows this to be a tagged pointer object Class getIsa(); uintptr_t isaBits() const; . } #include "isa.h" 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 is of type isa_t, and isa_t isa union that defines two CLS and bits members and a structural bit field ISA_BITFIELD (used to store class information and other information)

#   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 unused            : 1;                              \
      uintptr_t has_sidetable_rc  : 1;                              \
      uintptr_t extra_rc          : 8
Copy the code
Parameter names role The size of the The position of
nonpointer Whether to enable pointer optimization for isa Pointers 0: The pure ISA pointer contains only the address of class objects 1: the ISA contains the address, class information, and reference counting of class objects 1 0
has_assoc Is there an associated object? 0: No 1: exists 1 1
has_cxx_dtor Does the object have a destructor for C++ or Objc? If it does, it needs to do the destructor logic. If it doesn’t, it can get rid of the object faster 1 2
shiftcls Store the value of a class pointer. With pointer optimization turned on, 33 bits are used to store class Pointers in the ARM64 architecture 33 3 ~ 35
magic Used by the debugger to determine whether the current object is a real object or has no space to initialize 5 36 ~ 40
weakly_referenced Weak references 0: no 1: exists 1 41
deallocating 0: No. 1: Yes 1 42
has_sidetable_rc Whether to use the plug-in reference count, when the object reference technique is greater than 10 need to borrow this variable to store carry 1 43
extra_rc The referential count value of this object is actually the referential count value minus 1. If the object’s reference count is 10, extra_rc is 9. If the reference count is greater than 10, has_sideTABLE_rc is used 19 44 ~ 63

Isa storage distribution is as follows:

Initialize the core method

1. Initialize (newisa)

2. Member assignment (newisa.bits = ISA_MAGIC_VALUE)

3. Class association (newisa.shiftcls = (uintptr_t)cls >> 3)

inline void objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor) { ASSERT(! isTaggedPointer()); if (! nonpointer) { isa = isa_t((uintptr_t)cls); } else { ASSERT(! DisableNonpointerIsa); ASSERT(! cls->instancesRequireRawIsa()); isa_t newisa(0); #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 newisa.has_cxx_dtor = hasCxxDtor; newisa.shiftcls = (uintptr_t)cls >> 3; #endif // 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

newisa.shiftcls = (uintptr_t)cls >> 3; Why do I need to move 3 to the right? This is mainly because Shiftcls is in the middle of the ISA pointer address, and there are three bit fields in front of it. In order not to affect the data in the first three bit fields, it needs to be zeroed by right shift.

Validation of isa pointer to class association:

Object_getClass by viewing the object_getClass source code

object_getClass -> getIsa -> ISA

Class object_getClass(id obj) { if (obj) return obj->getIsa(); else return Nil; } inline Class objc_object::getIsa() { if (fastpath(! isTaggedPointer())) return ISA(); extern objc_class OBJC_CLASS_$___NSUnrecognizedTaggedPointer; uintptr_t slot, ptr = (uintptr_t)this; Class cls; slot = (ptr >> _OBJC_TAG_SLOT_SHIFT) & _OBJC_TAG_SLOT_MASK; cls = objc_tag_classes[slot]; if (slowpath(cls == (Class)&OBJC_CLASS_$___NSUnrecognizedTaggedPointer)) { slot = (ptr >> _OBJC_TAG_EXT_SLOT_SHIFT) & _OBJC_TAG_EXT_SLOT_MASK; cls = objc_tag_ext_classes[slot]; } return cls; } inline Class 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 } #if __ARM_ARCH_7K__ >= 2 || (__arm64__ && ! __LP64__) # define SUPPORT_INDEXED_ISA 1 #else # define SUPPORT_INDEXED_ISA 0 #endifCopy the code

As can be seen from the above, the acquisition of class is actually the sum operation of ISa. bits and ISA_MASK ISA_MASK

define ISA_MASK 0x0000000ffffffff8ULL
Copy the code

What is Isa.bits? See 👇

Why do you want and? Because isa isa consortium, bits 3-35 are used to store the class information. Isa needs to perform a bit operation to calculate the real address of the stored class, which is equivalent to reading only bits 3-35.

Two, through the bit operation

throughx/4gx objgetobjThe current class information is stored inisaIn the pointer, andisaIn theshiftclsThe number is 44 (for example, in the macOS environment)__x86_64__To read the 44-bit information in the middle, you need to perform a bit operation

1. Move 3 bits to the right, and erase all parts except 44 bits to the left. Their relative positions remain unchanged.

2. You need to move 20 places to the left in order to erase the highest 17 places.

3. Move the result 17 places to the right, back to the original position.The process is as follows

Isa to:

The process of verifying the relationship between ISA and objects

One, through the bit operation

Two, through the log

Persion *obj1 = [[Persion alloc]init]; // Class = object_getClass(obj1); // Class metaClass = object_getClass(Class); Object_getClass (metaClass) = object_getClass(metaClass); // rootRootMetaClass = object_getClass(rootMetaClass); NSLog(@" instance object == %@ %p",obj1,obj1); NSLog(@" class object == %@ %p",class,class); NSLog (@ "superclass object = = % @ % p", obj1. The superclass, obj1. The superclass); NSLog(@" metaClass == %@ %p",metaClass,metaClass); NSLog (@ "metaClass superclass object = = % @ % p", the metaClass. The superclass, metaClass. The superclass); NSLog (@ "root metaclass = = % @ % p", rootMetaClass, rootMetaClass); NSLog (@ "root metaclass superclass object = = % @ % p", rootMetaClass. The superclass, rootMetaClass. The superclass); NSLog (@ "spikes metaclass = = % @ % p", rootRootMetaClass, rootRootMetaClass); NSObject *obj2 = [[NSObject alloc]init]; NSLog (@ "NSObject parent class = = % @ % p", obj2. The superclass, obj2. The superclass); The following instance objects are generated: == <Persion: 0x281515560> 0x281515560 Class object == Persion 0x10074D858 Parent object == NSObject 0x1DA65DE08 metaclass == Persion 0x10074D830 Metaclass parent object == NSObject 0x1DA65DDe0 Root metaclass == NSObject 0x1DA65DDe0 Root metaclass parent object == NSObject 0x1DA65DE08 Root metaclass == NSObject 0x1DA65dDE0 NSObject's parent class == (null) 0x0Copy the code

From the above, we can draw the following conclusion

An object’s ISA points to its class object

The ISA of a class object points to its metaclass

The isa of the metaclass refers to the root metaclass

The root metaclass isa points to itself

NSObject’s parent class points to nil

The official flow chart is as follows