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
isa
The 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.
isa
The 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
- non
nonpointer
, just assign the valuecls
.- is
nonpointer
I’m going to do some initial assignment.
2. isa
Data 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
union
Union, a data type, takes up 8 bytes.- Federated features: memory sharing, or with
The mutex
Property, 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
nonpointer
Is it true or notisa
Pointer optimization on (we are now all optimization on), the value is 0: pureisa
Pointer; 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_assoc
Is there an associated object? Value 0 none, value 1 yes.has_cxx_dtor
Is there ac++
orobjc
Destructors, if there is a destructor, go through the destructor logic first, without the object is released faster.shiftcls
Store the value of the class pointer, when pointer optimization is turned on, inarm64
There are 33 bit storage class Pointers in the schema.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 points to or has ever pointed to oneARC
Objects without weak references can be freed faster.deallocating
Indicates whether the object is being released.has_sidetable_rc
When the object’s reference count is greater than 10, the variable is borrowed to store the carry.extra_rc
The 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
.
isa
Directional 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
objc
printobjc
Four segments of memory information. Extension:x/6gx
It prints 6 segments of memory information.p/t
P /t Prints binary information;p/o
Print octal information;p/x
Print 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 debugging
The $1
and$4
The values of are the same, validating the objectperson
theisa
Is, and classXDPerson
It’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 directly
isa.bits & ISA_MASK
Such a&
To get it.- And we can also understand
objc->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. isa
The 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
isa
Point 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.
isa
Point 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.
isa
From 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.
isa
Point 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 represents
isa
The 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
- Defining inheritance relationships
XDTeacher
-> XDPerson
-> NSObject
- Verifying inheritance relationships
XDTeacher
Parent class validation
(lldb) x/4gx XDTeacher.class
0x1000011f8: 0x001d8001000011d1 0x00000001000011a8
0x100001208: 0x00000001003a1e50 0x0000000000000000
(lldb) po 0x00000001000011a8
XDPerson
Copy the code
XDPerson
Parent class validation
(lldb) x/4gx 0x00000001000011a8
0x1000011a8: 0x001d800100001181 0x0000000100aff140
0x1000011b8: 0x00000001003a1e50 0x0000000000000000
(lldb) po 0x0000000100aff140
NSObject
Copy the code
NSObject
Parent 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
- 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.
- The memory address of the metaclass
XDTeacher metaclass
Memory 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 metaclass
Memory 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 it
XDTeacher metaclass
Inherited 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