preface

  • IOS low-level exploration — Alloc, init, new exploration
  • IOS Low-level Exploration – Memory byte alignment analysis
  • IOS Low-level Exploration – The nature of objects

isaThe derivation

1. Analyze data from memory segments

In this article, when we debug through LLDB, we don’t print Po directly to the first memory segment, but Po 0x00000001029570d0&0x0000000ffffffff8. To print out the object.

0x00000001029570D0 This memory segment is isa. The value 0x0000000FFFFFFFF8 is the value of the ISA_MASK mask.

2. Analyze from the essence of the object

In this article, we know that the essence of an object isa structure, and find the parent class at the bottom is isa, a member of objc_objcet.

isaThe initialization

1. Initialization process

In this article, we learned that a function called initIsa() is called.

inline void objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor) { assert(! isTaggedPointer());if(! nonpointer) { isa.cls = 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. Omit useless code... isa = newisa; }}Copy the code
  • nonnonpointer, just assign the valuecls.
  • isnonpointerI’m going to do some initial assignment.

2. isaData type analysis

Union isa_t {// Two default constructorsisa_t() {} isa_t(uintptr_t value) : bits(value) {} uintptr_t bits;#if defined(ISA_BITFIELD)Struct {// bit field ISA_BITFIELD; // definedin isa.h
    };
#endif
};
Copy the code
  • unionUnion, a data type, takes up 8 bytes.
  • Federated features: memory sharing, or withThe mutexProperty, which means assignmentcls, does not assign values to other members.

Analyze 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)

# elif __x86_64__
# define ISA_MASK 0x00007ffffffffff8ULL
# 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

The fields in ISA_BITFIELD are the same, but the bitfields corresponding to each field are different on different architectural platforms. Here’s what each field means

  • nonpointerIs it true or notisaPointer optimization on (we are now all optimization on), the value is 0: pureisaPointer; The value is 1: isa contains not only the address of the class object, but also the class information and reference count of the object.
  • has_assocIs there an associated object? Value 0 none, value 1 yes.
  • has_cxx_dtorIs there ac++orobjcDestructors, if there is a destructor, go through the destructor logic first, without the object is released faster.
  • shiftclsStore the value of the class pointer, when pointer optimization is turned on, inarm64There are 33 bit storage class Pointers in the schema.
  • magicUsed by the debugger to determine whether the current object is a real object or has no space to initialize.
  • weakly_referencedIndicates whether an object points to or has ever pointed to oneARCObjects without weak references can be freed faster.
  • deallocatingIndicates whether the object is being released.
  • has_sidetable_rcWhen the object’s reference count is greater than 10, the variable is borrowed to store the carry.
  • extra_rcThe value representing the reference count of the object, which is actually the reference count value minus 1. For example, if the reference count of the object is 10, the value of this variable is 9. If the value exceeds 10, use this variablehas_sidetable_rc.

isaDirectional analysis of

1. Association between objects and classes

XDPerson *person = [XDPerson alloc];
Copy the code

1.1 Debugging through the console

A brief introduction to the LLDB command

  • x/4gx objcprintobjcFour segments of memory information. Extension:x/6gxIt prints 6 segments of memory information.
  • p/tP /t Prints binary information;p/oPrint octal information;p/xPrint hexadecimal information.

ShiftCls in ISA_BITFIELD stores Pointers to classes.

(LLDB) x/4gx person // Prints the memory information of the object Person 0x1018482f0: 0x001D800100001129 0x0000000000000000 0x101848300: 0x00000001018483D0 0x0000000101848610 (LLDB) p/t XDPerson. Class // Print binary values of XDPerson (class)The $1= 0b0000000000000000000000000000000100000000000000000001000100101000 XDPerson (lldb) p/t 0x001d800100001129 // The binary value of person's ISA (long)$2 = 0b0000000000011101100000000000000100000000000000000001000100101001

(lldb) p/t $2>>3<<3 // Shiftcl has three bits in front of it and we need to move 3 bits right and then left to restore position (long)$3 = 0b0000000000011101100000000000000100000000000000000001000100101000

(lldb) p/t $3<<17>>17 // Because it is emulator _x86_64 shiftcl has 17 bits left to find and 17 bits right to restore position (long)$4 = 0b0000000000000000000000000000000100000000000000000001000100101000
Copy the code
  • We’ll see through the above debuggingThe $1and$4The values of are the same, validating the objectpersontheisaIs, and classXDPersonIt’s connected.

1.2 Debug the source code provided here for an objC with objc_getClass()

Class object_getClass(id obj)
{
    if (obj) return obj->getIsa();
    else return Nil;
}
Copy the code
inline Class 
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

We know objC is both! IsTaggedPointer can be located directly to the ISA() function.

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

The SUPPORT_INDEXED_ISA macro is defined as 0 on iOS.

  • You know that directlyisa.bits & ISA_MASKSuch a&To get it.
  • And we can also understandobjc->getIsa()And what we return isClass.

Let’s navigate directly to CLS through LLDB debugging.

(LLDB) x/4gx person // Prints the memory information of the object 0x101D047e0:0x001D800100001129 0x0000000000000000 0x101D047f0: 0x00007ffF9B5FF170 0x00000000C3000002 (LLDB) p/x xdPerson. classThe $1= 0x0000000100001128 XDPerson (LLDB) p/x 0x001D800100001129&0x0000000ffffff8 // Pass object isa & ISA_MASK (long)$2 = 0x0000000100001128
Copy the code

You can directly observe that the value of $1 and $2 are the same, and verify that the object’s ISA points to the class.

2. isaThe walk between classes and metaclasses

2.1 The nature of classes at the bottom

The first member of objC_class is isa. The first member of objc_class is isa.

typedef struct objc_class *Class;
struct objc_class : objc_object{};
Copy the code

We have seen that an object’s ISA refers to a class, thus binding the object to the class. So how will class ISA go?

We continue debugging through LLDB.

XDPerson *person = [XDPerson alloc];
Copy the code
  • isaPoint from an object to a class
(lldb) x/4gx person
0x10185eac0: 0x001d8001000011a9 0x0000000000000000
0x10185ead0: 0x000000010185eba0 0x000000010185ede0
(lldb) p/x 0x001d8001000011a9 & 0x0000000ffffffff8
(long) The $1 = 0x00000001000011a8
(lldb) po The $1
XDPerson
Copy the code

We print the memory information for person and get the class information from the object’s ISA & ISA_MASK. That is, ISA points from the class instance object Person to the class XDPerson.

  • isaPoint from class to metaclass
(lldb) x/4gx The $1
0x1000011a8: 0x001d800100001181 0x00000001000011f8
0x1000011b8: 0x00000001003a1e50 0x0000000000000000
(lldb) p/x 0x001d800100001181 & 0x0000000ffffffff8
(long) $2 = 0x0000000100001180
(lldb) po $2
XDPerson
Copy the code

We print the memory information of class XDPerson and get the information of another class through class isa&ISA_MASK. Isa points from class XDPerson to class XDPerson (in fact, this class is our XDPerson metaClass metaClass, which is not the same memory address as XDPerson in the first step). We compilers treat a class as an instantiation object of its metaclass, just as an object instantiates a pattern from a class.

  • isaFrom the metaclass to the root metaclass
(lldb) x/4gx $2
0x100001180: 0x001d800100aff0f1 0x00000001000011d0
0x100001190: 0x0000000101e142b0 0x0000000100000007
(lldb) p/x 0x001d800100aff0f1 & 0x0000000ffffffff8
(long) $3 = 0x0000000100aff0f0
(lldb) po $3
NSObject
Copy the code

We print memory information for the metaclass XDPerson and get information for the other metaclass NSObject through isa & ISA_MASK. Isa points from the metaclass XDPerson to the metaclass NSObject. So here we can print nsobjcr. class and see that the memory address of the metaclass NSObject is not the same as the memory address of nsobjcr. class.

  • isaPoint from the root metaclass to the root metaclass
(lldb) x/4gx $3
0x100aff0f0: 0x001d800100aff0f1 0x0000000100aff140
0x100aff100: 0x0000000101e146e0 0x0000000300000007
(lldb) p/x 0x001d800100aff0f1 & 0x0000000ffffffff8
(long) $4 = 0x0000000100aff0f0
(lldb) po $4
NSObject
Copy the code

We print memory information for the metaclass NSObject, and get information for the root metaclass NSObject through isa & ISA_MASK of the metaclass. Isa points from the metaclass NSObject to the root metaclass NSObject. And we can actually see that the NSObject in step 3 is the same memory address as the NSObject in step 4.

Through the LLDB debugging above we basically understand the isa pointing bitmap analysis, the following is the isa bitmap provided by Apple to do an explanation

  • The dotted line representsisaThe walk. Instance object -> class -> metaclass -> root metaclass -> root metaclass (root metaclass itself).
  • The solid line represents the inheritance. The important thing to notice here is that the parent of the root metaclass is NSObject.

Inheritance relationships

A complement to the structure of the class

struct objc_class { //Class isa Class superclass; . Ellipsis... }Copy the code

The second segment of memory of the class refers to the parent class.

Class inheritance relationships

  1. Defining inheritance relationships

XDTeacher -> XDPerson -> NSObject

  1. Verifying inheritance relationships
  • XDTeacherParent class validation
(lldb) x/4gx XDTeacher.class
0x1000011f8: 0x001d8001000011d1 0x00000001000011a8
0x100001208: 0x00000001003a1e50 0x0000000000000000
(lldb) po 0x00000001000011a8
XDPerson
Copy the code
  • XDPersonParent class validation
(lldb) x/4gx 0x00000001000011a8
0x1000011a8: 0x001d800100001181 0x0000000100aff140
0x1000011b8: 0x00000001003a1e50 0x0000000000000000
(lldb) po 0x0000000100aff140
NSObject
Copy the code
  • NSObjectParent class validation
(lldb) x/4gx 0x0000000100aff140
0x100aff140: 0x001d800100aff0f1 0x0000000000000000
0x100aff150: 0x000000010105b980 0x0000000100000003
(lldb) po 0x0000000000000000
<nil>
Copy the code

Verify that class inheritance is XDTeacher -> XDPerson -> NSObject -> nil.

The inheritance of metaclasses

  1. The memory address on the inheritance chain of a class
(lldb) x/4xg XDTeacher.class 0x1000011f8: 0x001d8001000011d1 0x00000001000011a8 0x100001208: 0x00000001003a1e50 0x0000000000000000 (lldb) po 0x00000001000011a8 XDPerson (lldb) x/4xg 0x00000001000011a8 0x1000011a8:  0x001d800100001181 0x0000000100aff140 0x1000011b8: 0x00000001003a1e50 0x0000000000000000 (lldb) po 0x0000000100aff140 NSObjectCopy the code

We are prepared to get all the memory addresses of the class’s parents first.

  1. The memory address of the metaclass
  • XDTeacher metaclassMemory address and the memory address of the parent of the metaclass
(lldb) p/x 0x001d8001000011d1 & 0x0000000ffffffff8
(long) $3 = 0x00000001000011d0
(lldb) x/4xg 0x00000001000011d0
0x1000011d0: 0x001d800100aff0f1 0x0000000100001180
0x1000011e0: 0x00000001018002a0 0x0000000300000003
Copy the code

We get the memory address 0x0000000100001180 of the parent of the XDTeacher metaclass

  • XDPerson metaclassMemory address
(lldb) x/4xg 0x00000001000011a8
0x1000011a8: 0x001d800100001181 0x0000000100aff140
0x1000011b8: 0x00000001003a1e50 0x0000000000000000
(lldb) p/x 0x001d800100001181 & 0x0000000ffffffff8
(long) $4 = 0x0000000100001180
Copy the code

The memory address of the XDPerson metaclass is 0x0000000100001180, which is the same as the memory address of the parent of the XDTeacher metaclass.

  • And this is where we can verify itXDTeacher metaclassInherited fromXDPerson metaclass.
  • All validation
X /4xg 0x0000000100001180 0x100001180: 0x001D800100AFF0F1 0x0000000100AFF0F0 -->XDPerson's metaclass parent memory address 0x100001190: 0x0000000101D09E50 0x0000000300000003 // Check NSObjec memory information (LLDB) x/4xg 0x0000000100AFF140 0x100AFF140: 0x001d800100aff0f1 0x0000000000000000 0x100aff150: 0x000000010183a780 0x0000000100000003 // Check NSObjec metaclect memory address (LLDB) p/x 0x001D800100AFF0F1&0x0000000ffffff8 (long)A $5= 0x0000000100AFF0f0 -> Verify the metaclass of XDPerson ->NSObjec // Check the memory information of the root metaclass (LLDB) x/4xg 0x0000000100AFF0f0 0x100AFF0f0: 0x001d800100aff0f1 0x0000000100aff140 0x100aff100: 0x0000000101d094C0 0x0000000300000007 // Check the parent of the root metaclass (LLDB) x/4xg 0x0000000100AFF140 0x100AFF140: 0x001d800100aff0f1 0x0000000000000000 0x100aff150: NSObject = NSObject = NSObject = NSObject = NSObject = NSObject = NSObject NSObject (LLDB) p/x 0x001D800100AFF0F1&0x0000000ffffff8 (long)$7 = 0x0000000100aff0f0
(lldb) po $7
NSObject
(lldb) po 0x0000000000000000
<nil>
Copy the code

Through our step by step debugging our metaclass inheritance relationship comes out.

  • XDTeacher metaclass ->XDPerson metaclass ->NSObject->nil.

Through this step of the process, also verified isa bitmap, the author’s ability is limited, explore the deficiencies can be pointed out in the comments section.

The road of learning, forge ahead

The shortcomings can be pointed out in the comments section