Runtime and message forwarding in iOS

In the early 1980s, Xiao Li and Xiao Wang were long-distance lovers. Under the leadership of the horn of reform, Xiao Wang resolutely chose a city in the south to fight. At that time, there was no mobile phone. Rely on writing letters. However, because Xiao Wang travels frequently, his residential address will change frequently. Therefore, every time Xiao Li sends a reply to Xiao Wang, Xiao Wang may not receive it because of the change of address. Later, they thought of a good way to solve this problem. The specific method is as follows:

Message forwarding in the 80s

 

In fact, the above picture can basically express the function of Runtime in iOS and the message forwarding mechanism of iOS. The characteristic of Runtime is mainly the delivery of messages (methods). If the message (method) cannot be found in the object, it will be forwarded. How is it done? We explore the implementation mechanism of Runtime from the following aspects.

Runtime introduction

Objective-C extends the C language and adds object-oriented features and Smalltalk-style messaging mechanism. The core of this extension is a Runtime library written in C and compiled language. It is the cornerstone of Objective-C's object-oriented and dynamic mechanisms.

Objective-C is a dynamic language, which means that it not only requires a compiler, but also a runtime system to dynamically create classes and objects, and perform message transmission and forwarding. Understanding the Runtime mechanism of Objective-C can help us better understand the language, and when appropriate, we can expand the language to solve some design or technical problems in the project from the system level. To understand Runtime, we must first understand its core-messaging (Messaging).

To become an executable file, a high-level programming language needs to be compiled into assembly language and then assembled into machine language. Machine language is also the only language that the computer can recognize, but OC cannot be directly compiled into assembly language, but must be transcribed into pure C. The language is then compiled and assembled, and the transition from OC to C language is realized by runtime. However, we use OC for object-oriented development, and C language is more process-oriented development, which requires the conversion of object-oriented classes into process-oriented structures.

The above are all official document interpretations, which are a bit obscure and boring, so let's use the code to explain in detail.

Runtime messaging

For an object method  [obj test] , the compiler converts it into a message to send objc_msgSend(obj, test) . The process executed at Runtime is like this:

1. First, find its class through the isa pointer of obj ;

2. Find test in the method list of class ;

3. If there is no test in the class , continue to search for its superclass ;

4. Once the function test is found , execute its implementation IMP

Of course, due to efficiency issues, it is not reasonable to traverse the objc_method_list once for each message. Therefore, it is necessary to cache frequently called functions to improve the efficiency of function query. This is what objc_cache, another important member of objc_class, does-after finding the test, save the test's method_name as the key and method_imp as the value. When the test message is received again, it can be found directly in the cache to avoid traversing the objc_method_list. From the previous source code, you can see that objc_cache exists in the objc_class structure.

The method of objec_msgSend:

OBJC_EXPORTidobjc_msgSend (idself, SEL op, ...)

Let's take a look at the structure of objects, classes, and methods:

Class object (objc_class)

The Objective-C class is represented by the Class type, which is actually a pointer to the objc_class structure

The struct objc_class structure defines many variables. The structure saves the pointer to the parent class, the name of the class, the version, the instance size, the list of instance variables, the list of methods, the cache, the list of compliance protocols, etc. It can be seen that the class object is a structure struct objc_class, this structure The data stored in the body is metadata.

Instance (objc_object)

 

The metadata in the class object stores the relevant information about how to create an instance, which is created from the structure pointed to by the isa pointer. The isa pointer of the class object points to the metaclass.

All the information needed to create class objects and class methods are saved in the metaclass, so the entire structure should be as shown in the following figure:

Instance object, class object and metaclass diagram

The isa pointer of the struct objc_object structure points to the class object;

The isa pointer of the class object points to the metaclass;

The super_class pointer points to the class object of the parent class;

The super_class pointer of the metaclass points to the metaclass of the parent class;

It feels like a tongue twister, so it can be represented by a god map on the Internet:

Figure 6 Self-closed loop of instance object, class object and metaclass

From the figure above, we can see that the entire system constitutes a self-closed loop. If it is inherited from NSObject, the Root class in the figure above is NSObject.

 

c1 is a Class obtained through an instance object. The instance object can obtain its class object. The class name represents the class object when it is the recipient of the message. Therefore, the class object obtains the Class itself.

If we want to get the object of the ISA pointer, we can use the following two functions

OBJC_EXPORTBOOLclass_isMetaClass(Classcls) OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);

OBJC_EXPORTClassobject_getClass(idobj) OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);

class_isMetaClass is used to determine whether the Class object is a metaclass, and object_getClass is used to obtain the object pointed to by the isa pointer of the object.

 

It can be seen from the code that the Class obtained by an instance object through the class method is the class object pointed to by its isa pointer, while the class object is not a metaclass, and the object pointed to by the isa pointer of the class object is a metaclass.

So about the Runtime part, let’s summarize: First, the instance object is a structure. This structure has only one member variable, pointing to the class object that constructed it. This class object stores all the information needed by the instance object, including instance variables, Instance methods, etc., while class objects are created through metaclasses, class variables and class methods are stored in the metaclass, which perfectly explains how the entire class and instance are mapped to the structure. So to understand Runtime is to understand the data storage of iOS and the relationship and function among its classes, instances, class objects, and metaclasses at runtime.

Message forwarding mechanism

 The above mentioned a lot of basic understanding and concepts of Runtime. What does it have to do with message forwarding, and how to use it? This is about the iOS message forwarding mechanism.

In the final analysis, all method calls in Objective-C are essentially sending messages to objects.

1. Create a method in the class-(void)todoSomething;

2. The iOS system creates a number for this method: SEL(todoSomething); and adds it to the method list. (Selector is an instance of SEL, which is different from IMP, which is a pointer to the memory address of the final implementation program)

3. When calling this method: [Object todoSomething]; The system goes to the method list to insert this method number, and execute it when found.

Note: When we write C code, we often use function overloading, that is, the function name is the same but the parameters are different, but this is not feasible in Objective-C, because the selector only remembers the name of the method and no parameters. So there is no way to distinguish between different methods.

So if a method is called, the message will be sent once, and the method list will be searched in the related class object. If it is not found, it will search up the inheritance tree until the root of the inheritance tree (usually NSObject) is found. If it fails and the message forwarding fails, execute the doesNotRecognizeSelector: method and report an unrecognized selector error. So what exactly is message forwarding? Next, we will introduce the last three opportunities one by one.

1. Dynamic method analysis

Objective-C will call +resolveInstanceMethod: or +resolveClassMethod: at runtime, giving you the opportunity to provide a function implementation. If you add a function and return YES, the runtime system will restart the message sending process. Example as shown below

Printed out "Doing foo"

It can be seen that although the foo: function is not implemented, we dynamically add the fooMethod function through class_addMethod and execute the IMP of the fooMethod function. From the printed results, it was successfully realized.

If the resolve method returns NO, the runtime moves to the next step: forwardingTargetForSelector.

Alternate recipient

If the target object implements -forwardingTargetForSelector:, Runtime will call this method at this time to give you the opportunity to forward this message to other objects.

An example of implementing an alternate receiver is as follows:

You can see that we forwarded the current ViewController method to Person for execution through forwardingTargetForSelector. The printed result also proves that we successfully realized the forwarding.

Complete message forwarding

If the unknown message cannot be processed in the previous step, the only thing that can be done is to enable the complete message forwarding mechanism.

First, it will send the -methodSignatureForSelector: message to get the parameter and return value type of the function. If -methodSignatureForSelector: returns nil, Runtime will issue a -doesNotRecognizeSelector: message, and the program will hang at this time. If a function signature is returned, Runtime will create an NSInvocation object and send a -forwardInvocation: message to the target object.

Also prints "Doing foo"

This is the three forwarding process of Runtime. Let's talk about the practical application of Runtime

 

When the system's built-in method functions are not enough, you can extend some functions to the system's built-in method and keep the original functions. For example, I want to know whether the current URL is empty. If I judge it every time, it will be very troublesome. If I create an extension to write, I don't know how it is implemented internally.

1. The runtime exchange method can be used.

Two, you can also add methods dynamically

Three, add attributes to the classification

 

Four, KVO realization

The implementation of KVO relies on the powerful Runtime of Objective-C. When an object A is observed, the KVO mechanism dynamically creates a subclass of the current class of object A, and overrides the setter method of the observed property keyPath for this new subclass. The setter method is then responsible for notifying the changed status of the observed object's properties.

Five, message forwarding (hot update) to solve the bug (JSPatch)

Regarding message forwarding, message forwarding is divided into three levels. We can implement the replacement function at each level to achieve message forwarding, so as not to cause a crash. JSPatch can not only realize message forwarding, but also realize a series of functions of method addition and replacement.

Six, realize NSCoding's automatic archiving and automatic unarchiving

Principle description: Use the functions provided by runtime to traverse all the attributes of the Model itself, and perform encode and decode operations on the attributes.

Core method: rewrite the method in the base class of Model:

 

Summary: In the entire Objective-C operation, all method calls are the process of sending or forwarding messages. Finally, the first picture can be roughly turned into the following, which is easy to understand

 



Author: Jaren_lei
link: https: //www.jianshu.com/p/45db86af7b60

Guess you like

Origin blog.csdn.net/wangletiancsdn/article/details/97939901