It doesn’t matter how slowly you learn, as long as you’re not marking time, that’s progress!
preface
In the previous chapter, we explored the nature of objects and the process of isa relating to classes. We learned that an object is essentially a pointer to a structure and is associated to a class through its internal member variable isa. What isa class? So let’s move on to this chapter.
Class (Class)
In OC, everything is an object, and there is an ISA pointing to a class. In OC, there is an ISA pointing to a class, and there is an ISA pointing to a class.
isa
Point to the
-
object
isa
Point to the
Create an LhkhPerson class and instantiate an object p
If the object isa points to the class object ISA -> class, then the address of the class is 0x0000000100794520. Next LLDB debugging:
-
class
isa
Point to the
LhkhPerson (LhkhPerson, LhkhPerson, LhkhPerson, LhkhPerson, LhkhPerson); So the isa of the class –> a class with the same name as the class.
-
class
VSThe same class
So that makes us wonder why the pointer address is different, because it’s all LhkhPerson. Which is the actual pointer address of LhkhPerson? Let’s verify this with code:
The address of LhkhPerson is 0x00000001007944F8. The address of LhkhPerson is 0x00000001007944F8.
-
The metaclass
The birth of
The MachOView tool is used to analyze the machO executable
Then right click on theshow in Finder
, right click to display the package contents, and drag the executable file toMachOView
On the tools,
Through MachOView analysis, it can be seen that after the project is compiled, by looking at the symbol table, it is found that the system generates a metaclass for LhkhPerson, and the class corresponding to the problem left above is this metaclass.
-
The metaclass summary
- From the above analysis,
The metaclass
Is generated by system compilation,class
And its associatedThe metaclass
Is the same; - The object’s
isa
Point to theclass
.class
theisa
Point to theThe metaclass
.
- From the above analysis,
-
The metaclass
isa
Point to the
Does the metaclass have a class structure, or to whom does isa refer? Let’s move on to this question:
Continue through thelldb
foundThe metaclass
theisa
Point to theA metaclass
And theA metaclass
theisa
Point to theA metaclass
oneself
-
isa
To summarize
After debugging the above code and analyzing the MachOView tool, we finally get the point of ISA:
Object – isa – > class – isa – > metaclass – isa – > root metaclass – isa – > root metaclass
This gives you an ISA pointing graph
The pointing diagram of ISA has been explored. We usually have class inheritance in projects, so how does class inherit from 1?
class
Class inheritance chain
– Class inheritance chain
LhkhTeacher
Is inherited fromLhkhPerson
.LhkhPerson
Is inherited fromNSObject
, by printingLhkhTeather
andLhkhPerson
There areNSObject
And the inheritance chain between them isLhkhTeather
Inherited – > — –LhkhPerson
— — — — — — inheritance>NSObject
Since classes have a chain of inheritance, what about metaclasses?
– Metaclass inheritance chain
LhkhPerson inherits from NSObject, so we print out its metaclass, the root metaclass, the root metaclass address is 0x7FFF80030638, and the LhkhPerson metaclass’s parent print address is also 0x7FFF80030638, and we get that address pointing to NSObject, The root metaclass; Is it possible to get any metaclass whose parent class is the root metaclass?
LhkhTeacher
Is inherited fromLhkhPerson
.LhkhPerson
Inherited fromNSObject
, then if according to the above conclusion isLhkhTeacher
The parent class of the metaclass is also the root metaclass, but the print tells us that this is not the case. The parent class of the metaclass of the subclass is the metaclass of the parent class of the subclass. Why is this? Because metaclasses also have a chain of inheritance,LhkhTeacher metaclass
Inherited – > — –LhkhPerson metaclass
Inherited – > — –A metaclass NSObject
. Now, the question I think you’re going to ask is, the chain of class inheritance goes to the root class NSObject, the metaclass inheritance goes to the root metaclass NSObject, so what do the root class NSObject and the root metaclass NSObject inherit from?
– Classes and metaclasses inherit special cases
According to the printed results, the parent class of the root class is null, that is, the root class has no parent class, and the parent class of the root metaclass is the root class.
– Summary of class inheritance chain
A subclass
—inheritance—>The parent class
—inheritance—>The root class
—>nil
Subclasses metaclass
—inheritance—>The parent class metaclass
—inheritance—>A metaclass
—inheritance—>The root class
—>nil
This gives you the inheritance diagram of the class
– Apple official ISA and class direction diagram
Class
Before exploring the underlying structure of classes we need to look at memory offsets
Memory offset supplement
To explore this, let’s use three examples: plain pointer, object pointer, and array pointer.
1. Common pointer
Two local variables are defineda
andb
They’re all equal to 10, and you can see by printing that they all have a value of 10, and their addresses are different,a
The address for0x7ffee9501c24.b
The address for0x7ffee9501c20
The draw
- According to the
a
andb
Is a local variable, and its address0x7
We know at the beginning that it’s stored inThe stack area
; - The stack area address is from high to low, and
a
andb
Difference between4
Bytes, becausea
forint
Type, need to occupy4
Bytes, soa
tob
The offset of is4
Bytes.
2. Object pointer
We define two objects, obj1 and obj2, and we analyze them by printing the addresses:
- It can be seen that
obj1
The address for0x600002440100Is through thealloc
It’s opened up, andobj2
The address for0x600002440110throughalloc
Open up, through0x6
The beginning and they are manually created and we know that they are in the heap; - Let’s look at the take address of the two, which is the point
obj1
andobj2
The addresses of heap addresses, respectively, are0x7ffeec5bcc08and0x7ffeec5bcc00You can see that is in the stack area, and becauseobj1
Bit object occupation8
Bytes, soobj1
toobj2
The offset of is8
Bytes.
3. Array pointer
Define an array pointerc
, and a pointerd
, the address of the array pointer C can be obtained by printing0x7ffeebfa1c20And thec
The address of the first element pointer to0x7ffeebfa1c20, the following element address is also every other4
Bytes (because array Pointers contain ints, ints are 4 bytes); The pointerd
The initial assignment isc
, so printd
The address is0x7ffeebfa1c20And d shifts by oneStep length
(The step size is also determined by the type of the element in C, which is an int, so offset by 4 bytes); From your analysis above, we can get:
- The first address of an array is the address of its first element, which is the memory address
The first address
It’s the address of the first element; - The address of the next element in the array can be obtained from the first address plus the offset of the previous element (depending on the type of element stored in the array)
The first address
+The offset
Method to get the address of the relative variable.
Class structure
In exploring the nature of the objectclass
The essence of aobjc_Class *
Structure pointer, searched directly in objc source codeobjc_Class *
Can be obtained;thenobjc_Class
What is it? Let’s search againobjc_Class
Volatilization can be defined in two ways
This one is no longer used in objC2, so just look at the one below
You can findobjc_Class
Is inherited fromobjc_object
.By analyzing the source code, we can find that in addition to some methods, there are four internal member variables,ISA
.superclass
.cache
.bits
:
ISA
: this is a hidden property inherited fromobjc_object
Member variables insideisa
, is a pointer to a structure, which takes up 8 bytes and is mainly used to associate classes;superclass
: Also aClass
Type, which is essentially a pointer to the structure, which is the parent class of the current class, which also takes 8 bytes;cache
: is acache_t
A type, essentially a structure, is used to optimize method calls, as discussed in a later section.bits
: is aclass_data_bits_t
A type, essentially a structure, is used to store the attributes, methods, and other data of a class, which is the focus of our discussion.
So if we want to talk about bits, we have to get the address of the memory, based on the memory offset above, to get the address of the bits, we need to get the size of the cache, because cache_t is a structure so the size of the memory depends on the internal variable, So we don’t know the size yet, so let’s look at the internal structure of cache_t:
cache
Memory size
Through the source codecommand
left-clickcache_t
There are many methods and static variables in the cache_t structure. Since methods and global variables are not stored in the structure, they are not important, so we only need to look at the following two variables:
explicit_atomic<uintptr_t> _bucketsAndMaybeMask
: is a generic structure whose true size is determined byuintptr_t
(unsigned long integer, 8 bytes), so the occupancy can be obtained8
Bytes;- A consortium
union
: contains only one variable, one structure,- There are three variables in this structure: we can conclude that the structure size is
8
byte_maybeMask
A:mask_t
(Uint32_t) type structure4
bytes_flags
:uint16_t
Type take up2
byte_occupied
:uint16_t
Type take up2
byte
_originalPreoptCache
: is a structure pointer type and occupies8
byte
If the union is mutually exclusive, then the size of the union is zero
8
Bytes. - There are three variables in this structure: we can conclude that the structure size is
union { struct { explicit_atomic<mask_t> _maybeMask; //4 #if __LP64__ uint16_t _flags; //2 #endif uint16_t _occupied; / / 2}; explicit_atomic<preopt_cache_t *> _originalPreoptCache; }Copy the code
So from the above analysis we can see that cache_t takes 8+8, which is 16 bytes, so we can calculate the addresses of bits, which is the first address +32 bytes offset (8+8+16).
bits
To explore the
Find out
class_rw_t *data() const {
return bits.data();
}
Copy the code
Class_rw_t: Class_rw_t: class_rw_t: class_rw_t: class_rw_t: class_rw_t: class_rw_t: class_rw_t
– class_rw_t
To explore the
Using the source code, we went into class_rw_t. There are many methods in class_rw_t. We found several familiar ones:
This is where we store our methods, our properties, our protocols, and so onlldb
To explore:
- We’re through
x/4gx p.class
Gets the class whose first address is0x100008b20; - Then through the above exploration out to want to obtain
bits
The address of is offset by 32 bytes, i.e0x20
, and forcibly convert toclass_data_bits_t*
, get the address is$2 = 0x0000000100008b40
; - through
return bits.data()
We also passedp $2->data()
get(class_rw_t *) $3 = 0x000000010126f810
So far we’ve gotclass_rw_t
The pointer address of; - Value by pointer address
* $3
The resultingclass_rw_t
The data in it.
So now that we haveclass_rw_t
Data structure, and we know it internally stored methods, properties, protocols, and so on, let’s see: this isLhkhPerson
Properties and methods
- attribute
- I got it from the top
class_rw_t
We take the value and get its property listp $4.properties()
; - And you just take it layer by layer
p $5.list
.p $6.ptr
, and then value by addressp *$7
That’s when we actually get our list of properties; - through
p $8.get(0)
Get the first property, get ourname
It’s the same as what we defined.
- Member variable supplement
We did not add member variables to the property sheet when we explored the properties above, so will the member variables also exist in the property sheet?
We found that we could only print oursname
Property, if I try to get it again, it’s going to be an out-of-bounds error, so member variablesnickName
It doesn’t exist in the property sheet, so where does it exist?
When we look at the implementation of the method class_rw_t, we find that there’s another method class_ro_t, and we go in and look at the implementation,
Class_ro_t = ivar_list_t *; class_ro_t = ivars; class_ro_t = ivar_list_t *
- We got that from the previous step
class_rw_t
Address and value, and then throughp $4.ro()
Access to theclass_ro_t
Address; - We’re through
class_ro_t
Address gets the value and then passes throughp $6.ivars
Gets the address of the list of instance variables and passesp *$7
The values; - through
p $8.get(0)
We got our instance variable in there, and we printed ournickName
Member variable; - So let’s go ahead and evaluate and find out that it’s still in there
_name
And we know thisname
Is the property that we defined, so we can also get what the system generates for the property_ the property name
Does not existclass_ro_t
theivar_t
In the.
conclusion
The class_rw_t method is called within the class_rw_t method, and the ivars (ivar_list_t) instance variable ivar_t is used to store the member variables and the _ attribute names of the system-generated attributes.
- methods
Let’s explore the method list in the same way:
Now, there’s one caveat here when we get the method name, becausemethod_t
The system does the processing, it’s a structure, and it puts data like the method name into a structure variable inside of itbig
Down, so we need to put one after the method name here.big()
.
- Class method supplement
So we go ahead and get the method, and after we find the getter and the setter of the property, we get out of bounds, but we don’t get the +(void)sayHello class method that we declared. What about the class method that we declared?
In fact, we know that the object method exists in the class, so we know that everything in OC is an object, then we guess that the class method will exist in the metaclass?
P /x object_getClass(lhkhperson.class); p/x object_getClass(lhkhPerson.class);
Object methods are stored in bits of the class structure, and class methods are stored in bits of the metaclass structure.
conclusion
Through the above exploration, we can know that the underlying structure of a class is mainly four variables ISA, superClass, cache and bits, and our bits store attributes, object methods, protocols and other data.