(Summary of articles on underlying principles of iOS)
(iOS)
The main purpose of this article is to analyze the structure of class & class. The whole article is about some exploration around a class
The analysis of the class
Class analysis is mainly to analyze isa direction and inheritance relationship
The preparatory work
Define two classes
- Inherited from
NSObject
The class ofCJLPerson
.
@interface CJLPerson : NSObject { NSString *hobby; } @property (nonatomic, copy) NSString *cjl_name; - (void)sayHello; + (void)sayBye; @end @implementation CJLPerson - (void)sayHello {} + (void)sayBye {} @endCopy the code
- Inherited from
CJLPerson
The class ofCJLTeacher
@interface CJLTeacher: CJLPerson @end @implementation CJLTeacher @end copy codeCopy the code
- Define two objects in main as two:
person & teacher
int main(int argc, const char * argv[]) { @autoreleasepool { //ISA_MASK 0x00007ffffffffff8ULL CJLPerson *person = [CJLPerson alloc]; CJLTeacher *teacher = [CJLTeacher alloc]; NSLog(@"Hello, World! %@ - %@",person,teacher); } return 0; } copy codeCopy the code
The metaclass
First, let’s introduce metaclasses through LLDB debugging in a case
- Run the program with a breakpoint in the CJLTeacher section of main
- Enable the LLDB debugging. The following figure shows the debugging process
Based on the debugging process, we have a question: Why is p/x 0x001d8001000022DD & 0x00007FFFFFFff8ULL and P /x 0x00000001000022B& 0x00007FFFFFFFF8ull in the figure All the class information printed out is CJLPerson, right?
0x001d8001000022dd
是person
The object’sIsa pointer address
, its&
After theThe results of
isCreate the person
The class ofCJLPerson
0x00000001000022b0
Is of the class to which the class information obtained in ISA refersisa
Pointer address of, i.eCJLPerson class in the class
的isa
Pointer address, in Apple, we just call itCJLPerson class in the class
forThe metaclass
- So, both prints
CJLPerson
Is becauseThe metaclass
As a result of
Description of metaclasses
To explain what metaclasses are, there are mainly the following points:
- We all know that
object
theisa
Is pointing toclass
.class
In fact, is also aobject
Can be calledClass object
, itsisa
The bitfield points to the apple-definedThe metaclass
The metaclass
issystem
To itdefine
andcreate
Are made ofThe compiler
And in the process,class
theattribution
From theThe metaclass
The metaclass
是Class object
的class
, eachclass
There is a uniqueThe metaclass
Used to storeClass methods
.The metaclass
Itself isnameless
, withclass
phaseassociated
, so it was usedA name that is the same as the class name
The following throughlldb
Command to exploreDirection of metaclasses
, that is,isa
theWalk a
, as shown in the figure below, a relationship chain can be obtained:Object -> class -> metaclass -> NSobject, NSobject points to itself
conclusion
As you can see from the picture
object
的isa
Point to theclass
(Also calledClass object
)class
的isa
Point to theThe metaclass
The metaclass
的isa
Point to theA metaclass
, i.e.,NSObject
A metaclass
的isa
Pointing to itoneself
How many NSObject are there?
As you can see from the figure, the last root metaclass is NSObject. Is this NSObject the same as the NSObject we know from development?
The following two verification methods are available
- 【 Method 1 】
lldb
Command to verify - 【 Method 2 】
code
validation
[Method 1] Verify using the LLDB command
We also use LLDB debugging to verify that the two NSObject are the same, as shown in the figure below
As we can see from the figure, the last metaclass of class NSObject is also NSObject, which is the same metaclass as the root metaclass (NSObject) of CJLPerson above. Therefore, we can conclude that there is only one root metaclass of class NSObject in memory, and the metaclass of the root metaclass refers to itself
【 Method 2 】 Code verification
Get the class in three different ways and see if they print the same address
Class class1 = [CJLPerson Class]; void classNum (){Class class1 = [CJLPerson Class]; Class class2 = [CJLPerson alloc].class; Class class3 = object_getClass([CJLPerson alloc]); NSLog(@"\n%p-\n%p-\n%p-\n%p", class1, class2, class3); } copy codeCopy the code
Here is the result of the code running
As you can see from the result, the address is the same, so there is only one copy of NSObject, that is, there is only one copy of NSObject in memory
[Interview question] : How many classes exist?
There is only one copy of a class object because the class information is always in memory
The famous ISA routing & inheritance diagram
Based on the above exploration and various tests,Object, class, metaclass, root metaclass
Is shown in the following figure
Isa walk a
The trend of ISA has the following points:
Instance of Subclass
theisa
Point to theClass (class)
Class object
isa
Point to theMeta class
Meta class
theisa
Point to theRoot Metal class Root Metal class
Root Metal class Root Metal class
的isa
Pointing to itoneself
In and of itselfThe closed loop
Here,A metaclass
isNSObject
Superclass walk a
The direction of superclass (i.e. inheritance relationship) is also illustrated in the following points:
-
Inheritance relationships between classes:
Class (ttf_subclass)
Inherited fromSuperClass (superClass)
SuperClass (superClass)
Inherited fromRootClass
In this case, the root class isNSObject
The root class
Inherited fromnil
, soThe root class
namelyNSObject
Be understood asOrigin of all things
Out of thin air
-
Metaclasses also have inheritance. The inheritance relationship between metaclasses is as follows:
Metal SubClass metaclass
Inherited fromThe metal SuperClass of the metal SuperClass
The metal SuperClass of the metal SuperClass
Inherited fromRoot Metal Class
Root Metal Class Root Metal Class
Inheritance inRoot class
In this case, the root class isNSObject
-
[Note] There is no inheritance relationship between instance objects, but between classes
For example
Mentioned earlierCJLTeacher and the target teacher
、CJLPerson and object Person
The following figure shows an example
-
Isa chain (two)
- Teacher’s ISA chain:
Teacher (subclass) --> CJLTeacher (subclass) --> CJLTeacher (subclass) --> NSObject (root metaclass) --> NSObject (root metaclass, self)
- Person isa bitmap:
Person (superclass object) --> CJLPerson (superclass) --> CJLPerson (superclass) --> NSObject (root metaclass) --> NSObject (root metaclass, self)
- Teacher’s ISA chain:
-
Superclass routing chain (two)
- Class inheritance chain:
CJLTeacher (subclass) --> CJLPerson(superclass) --> NSObject (root class) --> nil
- Metaclass inheritance chain:
CJLTeacher (child metaclass) --> CJLPerson(parent metaclass) --> NSObject (root metaclass) --> NSObject (root class) --> nil
- Class inheritance chain:
objc_class & objc_object
Now that the ISA move is clear, a new question arises: Why do objects and classes have ISA properties? There are two struct types that have to be mentioned: objc_class and objc_object
On the basis of these two structures, the above questions are explored below.
In the previous article ios-underlying principles 07: isa’s principles of class association, the main. M file was compiled using clang. The compiled c++ file shows the c++ source code below
-
The underlying compilation of NSObject is the NSObject_IMPL structure,
- Among them
Class
isisa
The type of the pointer is made up ofobjc_class
Defined type, - while
objc_class
It’s a structure. In iOS, all of themClass
Are based onobjc_class
Created for the template
- Among them
struct NSObject_IMPL { Class isa; }; typedef struct objc_class *Class; Copy the codeCopy the code
-
Search for the definition of objc_class in the objC4 source code. There are two versions of the definition in the source code
The old version
Located in theruntime.h
Has been abolished
- The new in
objc-runtime-new.h
This is,objc4-781
The newly optimized structure analysis of our later classes is also based on the new version.
From the new version of the definition, you can see that objc_class structure types inherit from objc_object,
-
Search objc_object (or objc_object {, which also has two versions) in objC4 source code
- A is located in the
objc.h
, has not been repealed from the compiledmain.cpp
As you can see, use this version of theobjc_object
- Located in the
objc-privat.h
- A is located in the
The following is the definition of objc_object in the compiled main.cpp
struct objc_object { Class _Nonnull isa __attribute__((deprecated)); }; Copy the codeCopy the code
What does objc_class have to do with objc_object?
Through the above source search and the low-level compilation of the source code in main. CPP, the following points are explained:
- Structure type
objc_class
Inherited fromobjc_object
Type, whereobjc_object
It’s also a structure, and it has aisa
Property, soobjc_class
Also has theisa
attribute - In the mian. CPP underlying compilation file,
NSObject
In theisa
At the bottom is made up ofClass
Defined by, whereclass
The underlying code fromobjc_class
Type, soNSObject
Also has theisa
attribute NSObject
Is a class that initializes aInstance object objc
Objc meetobjc_object
(that is, has the ISA attribute), mainly becauseisa
Is made up ofNSObject
从objc_class
I inherited it, andobjc_class
Inherited fromobjc_object
.objc_object
有isa
Properties. soobject
There is aisa
Isa saidPoint to the
, from the currentobjc_object
Objc_object (structure)
Is the currentThe root object
.All objects
They all have this characteristicobjc_object
That is, it has the ISA attribute
【 baidu interview question 】 The relationship between objc_object and objects
- All of the
object
Are based onobjc_object
As a templateinheritance
Come over - All objects are from
NSObject
(OC), but what really goes to the bottom is oneObjc_object (C/C + +)
The structure type of
【 Conclusion 】 The relationship between objc_object and objects is inheritance
conclusion
-
All objects + classes + metaclasses have ISA properties
-
All objects are inherited from objc_object
-
Everything is an object, and everything comes from objc_object. There are two conclusions:
- All to
objc_object
Created for the templateobject
, there areisa
attribute - All to
objc_class
Is created for the templateclass
, there areisa
attribute
- All to
-
At the structural level, it can be popularly understood as the connection between the upper OC and the bottom:
The lower
Is through theThe structure of the body
The definition of theThe template
, e.g.Objc_class, objc_object
The upper
It’s through the bottomThe template to create
For exampleCJLPerson
Objc_class, objc_object, ISA, object, NSObject
, as shown in the figure below
Class structure analysis
The main thing is to analyze what is stored in the class information
Supplementary knowledge – Memory offset
Before you can analyze the class structure, you need to understand memory offsets because they are used when accessing the class information
【 Common pointer 】
// Int a = 10; Int b = 10; NSLog(@"%d -- %p", a, &a); NSLog(@"%d -- %p", b, &b); Copy the codeCopy the code
The print result is shown in the following figure
- A and B point to 10, but a and B point to 10
The address is different
This is a copy that belongs toCopy the value
, also known asDeep copy
- The addresses of A and B differ by four bytes, depending on the type of a and B
Its address points to the figure
【 Object Pointer 】
// object CJLPerson *p1 = [CJLPerson alloc]; CJLPerson *p2 = [CJLPerson alloc]; NSLog(@"%d -- %p", p1, &p1); NSLog(@"%d -- %p", p2, &p2); Copy the codeCopy the code
The print result is shown in the figure
- P1 and P2 are Pointers,
p1
Is pointing to[CJLPerson alloc]
The address of the space created is the memory address. The same is true for P2 - & p1, and p2 is
The address of a pointer to objects P1 and P2
, the pointer isThe secondary pointer
Its pointer points to the following figure
[Array pointer]
Int c[4] = {1, 2, 3, 4}; int *d = c; NSLog(@"%p -- %p - %p", &c, &c[0], &c[1]); NSLog(@"%p -- %p - %p", d, d+1, d+2); Copy the codeCopy the code
The print result is as follows
&c
和&c[0]
Is to takeThe first address
, i.e.,The array name equals the first address
&c
与&c[1]
Difference between4
Bytes, the number of bytes between addresses, depending on the storageThe data type
- Can be achieved by
Initial address + offset
Fetch the other elements in the array, where the offset is the array’s index, and the first address in memory is the actual addressThe number of bytes moved is equal to the offset * number of data type bytes
Its pointer points to something like the following
Explore what’s in the class information
When exploring what’s in the class information, we don’t know what the structure of the class is beforehand, but can we get a first address from the class, and then get all the values from the address by translation
According to the new objc_class definition (objC4-781 version) mentioned above, it has the following properties
struct objc_class : objc_object { // Class ISA; //8 bytes Class superclass; //Class type 8 bytes cache_t cache; // formerly cache pointer and vtable class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags //.... Method partially omitted, not posted} copy codeCopy the code
isa
Property: inherited fromobjc_object
theisa
, accounting for8
bytesuperclass
Properties:Class
Type,Class
Is made up ofobjc_object
By definition, it’s onePointer to the
, accounting for8
bytecache
Property: simple slave typeclass_data_bits_t
We don’t know yet, andclass_data_bits_t
Is aThe structure of the body
Type,The structure of the body
theMemory size
Need toInternal properties
To make sure, andThe pointer to the structure is 8 bytes
bits
Attributes: OnlyThe first address
By translating the sum of the memory sizes of the above three properties, it can be obtainedbits
Calculate the memory size of the cache class
Going into the definition of cache_t (which only posts properties that are not static on the structure, mainly because static properties do not exist in the memory of the structure), we have the following properties
struct cache_t { #if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_OUTLINED explicit_atomic<struct bucket_t *> _buckets; Explicit_atomic <mask_t> _mask; // is type mask_t, which is an alias for an unsigned int, #elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16 explicit_atomic<uintptr_t> _maskAndBuckets; Mask_t _mask_unused; #if __LP64__ uint16_t_flags; // uint16_t = 1 byte; // uint16_t = 1 byte; // Uint16_t is an alias for unsigned short and takes 2 bytes to copy codeCopy the code
-
Calculate the memory size of the first two attributes, and the final memory size will add up to 12 bytes in both cases
-
[Case 1] IF process
buckets
Type isstruct bucket_t *
, it isStructure pointer
The type,8
bytemask
是mask_t
Type, andmask_t
是unsigned int
The alias of, occupy4
byte
-
[Case 2] Elseif process
_maskAndBuckets
是uintptr_t
Type, it’s aPointer to the
, accounting for8
byte_mask_unused
是mask_t
Type, andmask_t
是uint32_t
The alias of the type definition4
byte
-
-
Uint16_t is an alias for unsigned short and takes up 2 bytes
-
Uint16_t is an acronym for unsigned short and it’s 2 bytes long
Summary: So finally calculate the cache class memory size = 12 + 2 + 2 = 16 bytes
To obtain bits
Therefore, to obtain the contents of bits, we simply need to shift 32 bytes from the first address of the class
The following is approvedlldb
Command debugging
-
There are two ways to get the first address of a class
- through
p/x CJLPerson.class
Get the initial address directly - through
x/4gx CJLPerson.class
To print memory information
- through
- One of the
data()
Get the data fromobjc_class
Methods provided
- from
$2
This can be seen in the printout of the pointerbits
Of the typeclass_rw_t
, is also a structure type. But we still don’t see itProperty list, method list
Wait, need to continue to explore
Explore the property list, namely property_list
By looking at theclass_rw_t
Defined source discovery,The structure of the body
There areprovide
The correspondingmethods
To obtainProperty list, method list
Etc., as shown below
inTo obtain bits
And print thebits
Information based throughclass_rw_t
Provide ways to continue exploringbits
In theProperty list
The following is a graphical representation of the LLDB exploration process
p $8.properties()
In the commandpropertoes
Methods byclass_rw_t
Provided in the methodreturn
The actualtype
forproperty_array_t
- Because the list is of type
property_list_t
Is a pointer, so throughp *$10
Get memoryinformation
And it also proves thatbits
Stored in theproperty_list
, that is, the property list p $11.get(1)
, want to getCJLPerson
In theMember variable ' 'bobby
, will report error, promptAn array
The instructions of,property_list
There is only one property incjl_name
[Problem] Explore the storage of member variables
The difference between property_list and member variables is whether there are set and get methods. If there are methods, it is a property; if not, it is a member variable.
So the question is, where are member variables stored? Why does this happen? Move on to the analysis and exploration at the end
Explore the list of methods, methods_list
Preparation: Add two methods (instance method & class method) to CJLPerson mentioned earlier
//CJLPerson.h @property (nonatomic, copy) NSString *cjl_name; - (void)sayHello; + (void)sayBye; @end // cjLPerson. m @implementation CJLPerson - (void)sayHello {} + (void)sayBye {} @endCopy the code
LLDB debugging is also used to get the list of methods, as shown in the figure
- through
p $4.methods()
Get specificMethods list
thelist
Structure, in whichmethods
Is alsoclass_rw_t
Methods provided - printable
count = 4
You know, stored4
One way, can passp $7.get(i)
memoryThe offset
To obtain a single method, the scope of I is0-3
- If you’re printing
p $7.get(4)
To obtain the fifth method, the error is also reportedAn array
The exploration of new problems
[Problem] Explore the storage of member variables
Property_list contains only properties and no member variables. Where are the member variables stored? Why does this happen?
In addition to methods, properties, and protocols, there is also a ro method whose return type is class_ro_t. By looking at the definition of class_rw_t, the class that stores data in the bits property of objc_class, we can find that in addition to methods, properties, and protocols, there is also a ro method whose return type is class_ro_t. We can make a guess as to whether a member variable is stored in the ivars attribute of type ivar_list_t.
The following is the debugging process for LLDB
class_ro_t
The attributes in the structure are shown below, which you want to getivars
Which requires roThe first address is shifted by 48
byte
struct class_ro_t { uint32_t flags; //4 uint32_t instanceStart; //4 uint32_t instanceSize; //4 #ifdef __LP64__ uint32_t reserved; //4 #endif const uint8_t * ivarLayout; //8 const char * name; / / 1? 8 method_list_t * baseMethodList; // 8 protocol_list_t * baseProtocols; // 8 const ivar_list_t * ivars; const uint8_t * weakIvarLayout; property_list_t *baseProperties; // Method omit} copy codeCopy the code
As can be seen from the figure, the ivars attribute obtained, in which the count is 2, is found by printing that there are not only hobby but also name in the member list, so the following conclusions can be drawn:
- through
{}
The definition of theMember variables
, will be stored in the classbits
Property, throughbits --> data() -->ro() --> ivars
To obtainList of member variables
In addition to including member variables, it also includes the member variables defined by the property - through
@property
The attributes defined will also be stored in the bits attribute throughbits --> data() --> properties() --> list
To obtainProperty list
, which contains only properties
[Problem] Explore the storage of class methods
From this we can see that the Methods list has only instance methods and no class methods, so the question is, where are the class methods stored? Why does this happen? So let’s break it down a little bit
In the first half of the article, we mentionedThe metaclass
.Class object
theisa
Point isThe metaclass
.The metaclass
Is used toStorage class
So we guessed:Whether class methods are stored in bits of the metaclass
? Can be achieved bylldb
Command to verify our guess. The following figure shows the debugging flow of the LLDB command
By printing the metaclass method list in the figure, we can see that our guess is correct, so we can draw the following conclusions:
class
theInstance methods
Stored in theThe bits attribute of the class
throughbits --> methods() --> list
To obtainList of instance methods
, e.g.CJLPersong
Of the classThe instance method sayHello
It is stored in theCJLPerson type bits
Property, classMethods list
In addition to includingInstance methods
, but also include propertiesSet method
和The get method
class
theClass method
Stored in theThe bits attribute of the metaclass
throughMetaclass bits --> methods() --> list
To obtainClass method list
, e.g.CJLPerson
In theClass methods sayBye
It is stored in theCJLPerson
Of the classThe metaclass
(also named CJLPerson)bits
Properties of the