Runtime Series
Runtime Principle Exploration (I) — An in-depth understanding of ISA (Apple isa optimization)
An in-depth analysis of the Class structure
OC Class cache_t
Runtime principle exploration (4) — To investigate the bottom message mechanism
Exploring the Runtime principle (5) — The nature of Super
Runtime theory exploration (6) — Runtime in the interview question
I am inThe nature of the OC object (part 2) — the ISA&Superclass pointerThe memory structure of the Class object is described in the following figureThis article uses that as a starting point to take a closer look at the treasures inside a Class.
(a) A brief description of the structure of Class
First let’s take a look at objC’s source code definition of Class
struct objc_class : objc_object {
// Class ISA;
Class superclass;
cache_t cache; // formerly cache pointer and vtable method cache
class_data_bits_t bits; // class_rw_t * plus custom RR /alloc flags is used to obtain specific class information
class_rw_t *data(a) {
return bits.data();
}
void setData(class_rw_t *newData) {
bits.setData(newData);
}
/ /... The following stack of method/function code can be ignored for the moment
};
Copy the code
The structure is clear
Class superclass;
Get the parent Class, the metaclass object, which is also a Class typecache_t cache;
— is method cachingclass_data_bits_t bits;
To get specific information about the class, seebits
I must have read mineRuntime isa insights (Apple isa optimizations)This article will certainly give you a deep understanding.- And then there’s one
class_rw_t *data()
Function, which gets the value of the classRead-write messageThrough theclass_data_bits_t
thebits.data()
Method, click on the method to see
class_rw_t* data(a) {
return (class_rw_t *)(bits & FAST_DATA_MASK);
}
Copy the code
As you can see, this is similar to how we used isa to get the address of the object. Here, we use class_datA_bits_t bits; And a FAST_DATA_MASK for &. Returns a pointer of type class_rw_t *. The source code for this type is as follows
struct class_rw_t {
// Be warned that Symbolication knows the layout of this structure.
uint32_t flags;
uint32_t version;
const class_ro_t *ro;
method_array_t methods; //⚠️⚠️⚠️ List of methods
property_array_t properties; //⚠️⚠️⚠️ Attribute list
protocol_array_t protocols; //⚠️⚠️⚠️ Protocol list
Class firstSubclass;
Class nextSiblingClass;
char *demangledName;
}
Copy the code
As we know, OC class method, attribute, protocol can be added dynamically, that is, read and write, from the above source, you can find that there is indeed a corresponding member to save method, attribute, protocol information. The structure name class_rw_t also implies that the above methods, attributes, and protocol information are readable and writable. Const class_ro_t *ro; const class_ro_t *ro; This is a pointer to that part of the content information. Again, look at the source code
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
uint32_t instanceSize;// The memory space occupied by the instance object
#ifdef __LP64__
uint32_t reserved;
#endif
const uint8_t * ivarLayout;
const char * name;/ / the name of the class
method_list_t * baseMethodList;// List of methods
protocol_list_t * baseProtocols;// Protocol list
const ivar_list_t * ivars;// List of member variables
const uint8_t * weakIvarLayout;
property_list_t*baseProperties; Property listmethod_list_t *baseMethods(a) const {
returnbaseMethodList; }};Copy the code
This structure contains some read-only information about the class
uint32_t instanceSize;
— Memory space occupied by the instance objectconst char * name;
– the name of the classconst ivar_list_t * ivars;
— A list of class member variables
Method_list_t * baseMethodList; baseMethodList; , protocol_list_t * baseProtocols; , property_list_t * baseProperties; This is the same method_array_t methods that we saw in class_rw_t; , property_array_t properties; , protocol_array_t separate protocols; What’s the connection? What’s the difference? With these questions in mind, the analysis continues.
First of all, the above part is summarized in a diagram as followsThis diagram can be interpreted as the internal structure of a Class in a steady state. But in fact, in the process of program startup and initialization, Class is not such a structure, we can analyze the source code. I am inThe underlying implementation of objective-C categoriesI have analyzed the source code execution path from objC initialization to the category information loading process, so I will not repeat it here, just make a brief statement, if you don’t understand, you can add through this article.
- First of all in
objc-os.mm
Find objc’s initialization function invoid _objc_init(void)
- Continue to enter the
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
The inside of themap_images
function - in
map_images
So let’s go into the functionmap_images_nolock
function - in
map_images_nolock
At the end of the function, enter_read_images
Function entry - in
_read_images
In the function implementation, you can see that before you process the category information, that’s the comment// Discover categories
The above part of the code, is the class initialization steps
// Realize newly-resolved future classes, in case CF manipulates them
if (resolvedFutureClasses) {
for (i = 0; i < resolvedFutureClassCount; i++) {
realizeClass(resolvedFutureClasses[i]);
resolvedFutureClasses[i]->setInstancesRequireRawIsa(false/*inherited*/);
}
free(resolvedFutureClasses);
}
ts.log("IMAGE TIMES: realize future classes");
Copy the code
Enter the core function realizeClass
static Class realizeClass(Class cls)
{
runtimeLock.assertWriting();
const class_ro_t *ro;
class_rw_t *rw;
Class supercls;
Class metacls;
bool isMeta;
if(! cls)return nil;
if (cls->isRealized()) return cls;
assert(cls == remapClass(cls));
// fixme verify class is not in an un-dlopened part of the shared cache?
ro = (const class_ro_t *)cls->data(); //-----⚠️⚠️⚠️ In the beginning, the data() of the class is directly class_ro_t
if (ro->flags & RO_FUTURE) {
// This was a future class. rw data is already allocated.
//-----⚠️⚠️⚠️ If the RW has allocated memory, then the RW points to CLS ->data(), and then the RO pointer to the RW points to the previous ro
rw = cls->data();
ro = cls->data()->ro;
cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
} else {
Allocate writeable class data. //-----⚠️⚠️⚠️ If rW has not allocated memory
rw = (class_rw_t *)calloc(sizeof(class_rw_t), 1); //-----⚠️⚠️⚠️ Allocates memory to the RWrw->ro = ro; The rw ro pointer to initial ro rw - > flags = RW_REALIZED | RW_REALIZING; cls->setData(rw);//-----⚠️⚠️⚠️ Adjust class data()} isMeta = ro->flags & RO_META; . . . }Copy the code
From this function, we can see that,
- In the beginning, Class has no read/write part, only read-only part, i.e
ro = (const class_ro_t *)cls->data();
- Next, space is allocated for reading and writing information, i.e
rw
And then throughrw->ro = ro;
willclass_rw_t
One’s ownro
The pointer points to the realro
Information.- through
cls->setData(rw);
willcls->data()
Make changes, and finally passFAST_DATA_MASK
Role torw
After completing the above steps, you are ready to handle the loading of category information. In the next analysis of Class, I will only deal with the method list part, the property and protocol part is actually similar to the method processing logic. Going back to our previous question, class_ro_t and class_rw_t both have lists of methods. What’s the difference between them?
(2) class_ro_t
In fact,class_ro_t
The read-only information that represents the Class is inherent in the Class itself and, more directly, written to it@interface
and@end
Between methods, attributes, and other information, of course, the most important role is to store the class member variable informationivars
And byconst
Decorator specifications are not modifiable, which is why the Runtime cannot dynamically add member variables, as determined by the underlying structure. I personally understand this part as static information for OC.class_ro_t
In themethod_list_t * baseMethodList; // List of methods
Is a one-dimensional array that contains the Class’s own methods.
(3) class_rw_t
In aclass_rw_t
After that, the category is processed, and the list of methods in the Class itself and the list of methods in the category are placed firstclass_rw_t
themethod_array_t methods
The list of Class methods is placed first and at the end of the list. The order in which category methods are added is the same as the order in which category files are compiled. I have detailed explanations for this part of the processThe underlying implementation of objective-C categoriesThis is explained in detail in the article. As a result,method_array_t methods;
Is a two-dimensional array, as shown below
(4) method_t
Above we have analyzed the structure of class_rw_t and class_ro_t, and mainly focused on the method list. From the above analysis, we can find that the most basic and important unit is method_t, which contains all kinds of information needed to describe a method. Now, let’s do a thorough scan of it. First take a look at the definition in the source code
struct method_t {
SEL name;
const char *types;
IMP imp;
};
Copy the code
IMP IMP – a pointer to a function (i.e. the address of the method/function implementation), which is also defined in objc.h: typedef id _Nullable (*IMP)(id _Nonnull, SEL _Nonnull,…) ;
SEL name – We all know that SEL is a method selector, which can be interpreted as a method name, but what exactly is it? It is defined in objc. H: typedef struct objC_selector *SEL; It’s a pointer type that points to a struct objc_Selector, which unfortunately Apple doesn’t open source, but look at the code below
@implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; NSLog(@"%s",@selector(test)); } - (void)test { NSLog(@"%s",__func__); } @end ************************************* 2019- 08- 05 21:37:11.603121+0800 iOS-Runtime[2093:302816] test Copy the code
Struct objc_selector contains a char * member variable, and should be the first member of the structure. So we can think of SEL as a string, whose main function is to represent the name of the method. It can be converted to a string by the following method
- Can be achieved by
@selector()
andsel_registerName()
Convert a string toSEL
.- Can be achieved by
sel_getName()
andNSStringFromSelector()
willSEL
Convert to string- Methods with the same name in different classes have the same SEL, that is to say, for
XXX
Method, they all point to the same SEL in memorystruct objc_selector
Struct object, no matter how many classes define itXXX
Methods.
Const char *types — Encoding of function types (including return value types, parameter types). IOS provides an @encode directive that can represent specific types as string encoding, i.e. representing types by strings. The main purpose is to facilitate the runtime by describing and storing function return values and parameter types in strings. Take a look at the following code example
NSLog(@"%s",@encode(int)); NSLog(@"%s",@encode(float)); NSLog(@"%s",@encode(int *)); NSLog(@"%s",@encode(id)); NSLog(@"%s",@encode(void)); NSLog(@"%s",@encode(SEL)); NSLog(@"%s",@encode(float*)); * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *2019- 08- 06 16:13:22.136917+0800 iOS-Runtime[8904:779780] i 2019- 08- 06 16:13:22.137461+0800 iOS-Runtime[8904:779780] f 2019- 08- 06 16:13:22.137549+0800 iOS-Runtime[8904:779780] ^i 2019- 08- 06 16:13:22.137639+0800 iOS-Runtime[8904:779780] @ 2019- 08- 06 16:13:22.137718+0800 iOS-Runtime[8904:779780] v 2019- 08- 06 16:13:22.137832+0800 iOS-Runtime[8904:779780] : 2019- 08- 06 16:13:22.137912+0800 iOS-Runtime[8904:779780] ^f Copy the code
From the print above you can see the string representation for the various types. – (int)test:(int)age height:(float)height, We know OC method corresponding to the first two are the underlying function of the default parameter id self and SEL CMD, then the approach that in from left to right, return values and parameters of type int, respectively – > id – > SEL – > int – > float, converted to type code, is I – @ – : – I – f, The result is i24@0:8i16f20, which, you may wonder, contains some numbers that describe the length and position of the function’s arguments, from left to right:
i
The return type of the function isint
24
— Total length of arguments (24 bytes)@
— the first parameterid
0
— The initial memory offset of the first argument (0 bytes, from the 0th byte):
— The second parameterSEL
8
The offset in memory from the second parameter (8 bytes, starting from the 8th byte, so the aboveid
Arguments take up the previous 8 bytes)i
— The third parameterint
16
The start offset of the third parameter in memory (16 bytes, starting from the 16th byte, hence the aboveSEL
The parameter takes up the previous 8 bytes.f
— The fourth parameterfloat
20
— The start offset of the fourth parameter in memory (20 bytes, starting from the 20th byte, hence the aboveint
Arguments take up the first four bytes, and the total length is 24, so the last four bytes are givenfloat
For parameters)So, for any OC method, its
method_t
The values of the types string can be derived from the procedure above. You search in apple docsType Encoding
You can find more detailed introduction, which has all the parameter types corresponding to the string expression comparison table.Based on the above research, the three member variables in method_t provide all the information we need for an OC method, so that’s all we need to know about method_t.
(5) Cache_t method cache
To avoid getting too long, explore method caching by moving to the next article — Runtime Notes (3) — OC Class’s method cache cache_t
🦋🦋🦋 Portal 🦋 port 🦋
Runtime Principle Exploration (I) — An in-depth understanding of ISA (Apple isa optimization)
An in-depth analysis of the Class structure
OC Class cache_t
Runtime principle exploration (4) — To investigate the bottom message mechanism
Exploring the Runtime principle (5) — The nature of Super
Runtime theory exploration (6) — Runtime in the interview question