The ObjC Runtime 756.2 used on this page is from GITHUB

Began to continue to study OC source code, this research is isa initialization and pointing analysis.

1. The concept

What is the isa

Take a look at apple’s documentation:

isa

A Pointer to the class definition of which this object is an instance.

Isa: A pointer to the class of this object.

Open Xcode, find objc.h, and we can see the following code

#if ! OBJC_TYPES_DEFINED /// An opaque type that represents An objective-C class *Class; Struct objc_object {class _Nonnull ISA OBJC_ISA_AVAILABILITY; }; Typedef struct objc_object *id; /// A pointer to an instance of A class. #endifCopy the code

As you can see, Class is an objc_class structure.

Id, on the other hand, is a structure of type objC_Object.

2. Isa initialization

Before we do that, let’s review the flow chart for object initialization

Here, the instance isa is initialized, where CLS is the initialized class object, and hasCxxDtor is the destructor that contains C++ or not.

Let’s go into the function initIsa(CLS, true, hasCxxDtor) and see what is implemented internally

2.1 the isa notnonpointer

if (! nonpointer) { isa.cls = cls; }Copy the code
  • Nonpointer concept:

    Indicates whether pointer optimization is enabled for isa Pointers

0: pure ISA pointer

1: not only the address of the class object, but also the class information, reference count of the object, and so on.

At this point, if it isa pure isa pointer, the current class CLS is assigned to the binding property CLS of isa

Why is there this binding property, and what exactly is the structure of ISA?

Click isa.cls = CLS; View its structure as follows:

// Isa_t (uintptr_t value) : bits(value) {} CLS; uintptr_t bits; #if defined(ISA_BITFIELD) struct { ISA_BITFIELD; // defined in isa.h }; #endif };Copy the code

Isa isa union that contains

  • isa_tInitialization method
  • isa_t(uintptr_t value)The factory method
  • Class cls Binding properties
  • The structure of the bodyISA_BITFIELDA domain
  1. ISA_BITFIELD concept

    Struct ISA_BITFIELD = struct ISA_BITFIELD = struct ISA_BITFIELD

    # 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)
    Copy the code
  2. NONPOINTER_ISA rendering (hand-drawn, to be completed……)

  3. Restore the structure of ISA_t

    We now find that the overall structure of ISA can be replaced with something like this:

    union isa_t {
        isa_t() { }
        isa_t(uintptr_t value) : bits(value) { }
    
        Class cls;
        uintptr_t bits;
    #if defined(ISA_BITFIELD)
        struct {
            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;
            //         ISA_BITFIELD;  // defined in isa.h
        };
    #endif
    };
    Copy the code
  • Nonpointer: indicates whether pointer optimization is enabled for isa Pointers. 0: indicates pure ISA Pointers. 1: indicates that isa** contains not only the address of the class object, but also the reference count of the object.

  • Has_assoc: flag bit of associated object, 0 not present, 1 present (1 bit)

  • Has_cxx_dtor: does this object have a destructor for C++ or Objc? If it does, it needs to do the destructor logic. If it does not, it can release the object faster (1 bit).

  • ** 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. (33)

  • 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. (6)

  • Deallocating: Indicates whether the object is freeing memory (1 bit)

  • Has_sidetable_rc: when the object reference count is greater than 10, we need to borrow this variable to store carry (1 bit).

  • Extra_rc: When representing the reference count of this object, the reference count is actually subtracted by 1. For example, if the object’s reference count is 10, the extra_rc is 9. If the reference count is greater than 10, the following has_sideTABLE_rc is used. (1)

2.2 the isaisNonpointer type

        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

Copy the code
  • Generate a new ISA:

    isa_t newisa(0);

  • C++ destructor:

    newisa.has_cxx_dtor = hasCxxDtor; ‘specifies whether the current object has a C++ destructor. If not, it will quickly free memory.

  • A field assignment

    newisa.shiftcls = (uintptr_t)cls >> 3; Assigns the value of the stored pointer by moving it three bits to the right.

  • Returns the isa

    isa = newisa;

3. Direction of ISA

3.1 Pointing Diagram

As for the reference to ISA and the relationship between the subclass and its parent class, Apple provides an official picture as follows:

3.2 Code Analysis

3.2.1 of the objectisa

We execute a piece of code as follows and interrupt the line to the point:

Person *object = [Person alloc];
Copy the code

We know that isa — points to — > classes in objects.

3.2.2 of the classisa

Now we want to know the structure of the memory space of the class, execute the following command x/4gx person.class on the console, the result is as follows:

(lldb) x/4gx Person.class
0x100001130: 0x001d800100001109 0x0000000100b39140
0x100001140: 0x0000000101a46ed0 0x0000000200000007
Copy the code

Since isa is the first property of the class object, we know that 0x001D800100001109 is the isa of the changed object. Let’s see where it points to, using the p/x instruction:

(lldb) p/x 0x001d800100001109
(long) $16 = 0x001d800100001109
Copy the code

Oops, can’t see the results? What’s going on? The isa format of the class needs to be strong, so you can step back and try printing the address of the class:

po 0x100001130
Person
Copy the code

In memory, the first location of a class named Person points to the Person class.

No, no, the class that we’re pointing to here, we’ll call it a meta-class.

Class ISA — points to — > metaclass

3.2.3 metaclassisa

We now get the address of the metaclass, find isa’s MASK, 0x00007ffffFFFF8,

Enter the following command:

(lldb) p/x 0x001d800100001109 & 0x00007ffffffffff8
(long) $17 = 0x0000000100001108
(lldb) po 0x0000000100001108
Person
Copy the code

The metaclass address is 0x0000000100001108, printed in hexadecimal:

(lldb) x/4gx 0x0000000100001108
0x100001108: 0x001d800100b390f1 0x0000000100b390f0
0x100001118: 0x0000000100f5a480 0x0000000400000007
Copy the code

The isa pointer is 0x001d800100b390F1.

(lldb) p/x 0x001d800100b390f1 & 0x00007ffffffffff8
(long) $21 = 0x0000000100b390f0
Copy the code

0x0000000100b390f0 = 0x0000000100b390f0

po 0x0000000100b390f0
NSObject
Copy the code

At this point, we can see that the ISA of the metaclass points to its upper metaclass, the root meta-class, as NSObject.

So the metaclass isa points to the root metaclass

3.2.4 of the root metaclassisa

Let’s print the root metaclass structure:

x/4gx 0x0000000100b390f0
0x100b390f0: 0x001d800100b390f1 0x0000000100b39140
0x100b39100: 0x0000000101a47020 0x0000000500000007
Copy the code

Get its ISA and continue with the and operation with the mask

p/x 0x001d800100b390f1 & 0x00007ffffffffff8
(long) $27 = 0x0000000100b390f0
Copy the code

The result 0x0000000100b390f0 matches the root metaclass 0x0000000100b390f0.

At this point, we conclude that the isa of the root metaclass points to the root class NSObject.

What? You don’t believe me. This is all speculation. Verify it, okay?

Ok, create the following code

Void TestNSObject(){// NSObject instance object NSObject *object1 = [NSObject alloc]; // NSObject Class Class = object_getClass(object1); // NSObject metaClass = object_getClass(Class); // NSObject rootMetaClass Class rootMetaClass = object_getClass(metaClass); // NSObject rootMetaClass Class rootRootMetaClass = object_getClass(rootMetaClass); NSLog (@ "\ n \ n % p % p instance objects class p metaClass \ n \ n % % p root metaClass \ n % p spikes metaClass", object1, class, metaClass, rootMetaClass, rootRootMetaClass); }Copy the code

The print result is as follows:

0x10066DDC0 Instance object 0x7FFF9294A118 Class 0x7FFF9294A0F0 Metaclass 0x7FFF9294A0F0 Root metaclass 0x7FFF9294A0F0 Root metaclass 0x7FFF9294A0F0Copy the code

So, with the exception of the NSObject class, which is uniquely created, the metaclass, the root metaclass, the root metaclass, they’re all the same, because they’re all NSObject, so that proves it.

3.3 conclusion:

If we go back to this picture, the reddest one looks like this:

  • Isa to:

    • Object isa — > class
    • Isa — > metaclass in class
    • Isa —-> root metaclass in metaclass
    • Isa —-> root metaclass in root metaclass
  • Class inheritance:

    • Subclass — superClass — superClass
    • SuperClass — root metaclass
    • The root metaclass — superClass — NSObject
    • NSObject — superClass – nil