Objective-C Runtime Mechanism
Objective-C language is a dynamic language that enables many static languages to compile and link to the runtime for processing. At the same time, OC is also a simple language, a large part of which is the content of C. It only adds keywords and syntax at the language level. What makes OC really powerful is its runtime, it is very small but powerful, with the core being message distribution. The advantage of this dynamic language is that we are more flexible when writing code. For example, we can forward messages to the desired objects or exchange a method at will.
This feature means that OC requires not only a compiler, but also a runtime system to execute the compiled code. For OC, the runtime system is like an operating system. This Runtime system is called Runtime, which is written in C and sink. This Library gives the C language the object-oriented capability. The most important one is the message mechanism. In C language, function calls are compiled to determine which function to call. After compilation, the function is executed sequentially without any ambiguity. OC Function calling is called message sending and is a dynamic calling process. During compilation, you cannot determine which function to call. (It turns out that in the compilation phase, OC can call any function. Even if this function is not implemented, no error will be reported if it is declared. C language reports an error during compilation .) The corresponding function can be called only when the function is running.
In a word: the OC code we have compiled is actually a C language code converted into runtime during the program running process. runtime is the owner of OC. Runtime belongs to the bottom layer of OC and can perform very bottom-layer operations (which cannot be implemented with OC .)
The Runtime Library mainly does the following:
1. encapsulation: in this library, objects can be represented by struct in C language, while methods can be implemented using C functions. In addition, some additional features are added. After these struct and functions are encapsulated by the Runtime function, we can create, check, modify classes, objects, and their methods when the program is running.
2. final Execution Code of the method: when the program executes [object doSomething], it will send a message (doSomething) to the Message Receiver (object ), runtime will make different responses based on whether the message receiver can respond to the message.
There are currently two versions of OC Runtime: Modern Runtime and Legacy runtime. Modern Runtime, which are mainly used for 64-bit applications. Legacy runtime is mainly used for 32-bit applications. Currently, it is usually 64-bit.
Regarding the execution efficiency, "static language execution efficiency is higher than Dynamic Language execution efficiency" should be fine. Because some CPU computing losses are in the Runtime.
So how does OC implement dynamic calling? If the following code is written in OC:
[obj makeText];
Obj is an object and makeText is a function name. Execute a method. In some languages, the compiler will execute some extra optimizations and error checks, because the call relationship is very direct and obvious. However, for message distribution, it is not so obvious that you do not have to know whether an object can process messages before sending messages. If you send a message to him, the message may be processed or transferred to another Object for processing. A message does not have to correspond to a method. An object may implement a method to process multiple messages.
During compilation, RunTime will convert the above Code:
objc_msgSend(obj,@selector(makeText));
So in fact
Objc_msgSend (obj, @ selector (makeText); and
[obj makeText];
Is equivalent.
For example:
[Obj setTT: @ 111 isOK: YES];
And
Objc_msgSend (obj, @ selector (setTT: isOK :), @ 111, YES)
It is also equivalent. Note the parameter OC Function Name expression.
First, let's take a look at the object obj. In iOS, obj inherits from NSObject.
@interface NSObject{ Class isa OBJC_ISA_AVAILABILITY}
There is a Class isa pointer in NSObject. Then let's take a look at what Class is: this is defined in objc. h.
Typedef struct objc_class * Class; struct objc_class {Class isa; // points to metaclass Class super_class; // points to parent Class const char * name; // Class name long version; // class version information. The default initialization value is 0. You can use the runtime functions class_setVersion and class_getVersion to modify and read the information. long info; // some identification information, such as CLS_CLASS (0x1L) indicates that the class is a common class, including object methods and member variables; CLS_META (0x2L) indicates that the class is metaclass, including class methods; long instance_size; instance variable size of this class (including instance variables inherited from the parent class); struct objc_ivar_list * ivars; // address used to store each member variable; struct objc_method_list ** methodLists; // It is related to some flag bits of info, such as CLS_CLASS (0x1L), the object method is stored; struct objc_cache * cache; // a pointer to the recently used method to improve efficiency; struct objc_protocol_list * protocols; // The protocol used to store the class ;}
There are many things in a Class. Let's explain the important things:
Class isa: Point to metaclass, which is a static Class. Generally, isa in an Obj object points to a common Class, which stores common member variables and object methods (Methods Starting with-), and isa pointer in a common Class points to a static Class, static Class stores static type member variables and Class methods (Methods Starting with + ).
Class super_class: points to the parent Class. If this Class is the root Class, it is NULL.
The following figure shows the inheritance relationship between classes and objects:
.
Note: The isa pointer in all metaclass points to the Root metaclass (that is, Root Class Meta), while the Root metaclass points to itself. Root metaclass is generated by inheriting the Root Class. It is consistent with the root class structure member, that is, the structure mentioned above. The difference is that the isa pointer of Root metaclass points to itself.
Then let's take a look at the method:
@ Selector (makeText): This is a SEL method selector. SEL is mainly used to find the function pointer of the corresponding method through the method name (makeText) and then call its function. SEL itself is an int type address, which stores the method name. For a class, each method corresponds to a SEL. Therefore, there cannot be two methods with the same name in the iOS class. Even if the parameter type is different, because SEL is generated based on the method name, the same method name can only correspond to one SEL. At the same time, we should also know that there is no way to overload OC.
Let's take a look at how to dynamically find the corresponding method after a specific message is sent.
First, the compiler converts the Code [obj makeText] To objc_msgSend (obj, @ selector (makeText). In the objc_msgSend function, first, find the corresponding class through the isa pointer of obj. In the Class, first go to the cache and use SEL to find the corresponding function method. If the method is not found in the cache, go to methodList. If the method is not found in methodList, go to the superClass. If it can be found, add the method to the cache to facilitate the next search, and use the function pointer in the method to jump to the corresponding function for execution.
The method list of the class is actually a dictionary. The key is selector, and the value is the IMP function pointer. An IMP is a method implementation in the memory. It is very important that the relationship between selector and IMP is determined at runtime, rather than during compilation.
For object-oriented, everything is an object. In OC, classes are also objects. The class is Object, which can also process messages. So now you know why there are class methods and instance methods.
Method Swizzling
As we have mentioned above, the method consists of two parts. Selector is equivalent to the id of a method, and IMP is the implementation of the method. In this way, the relationship between selector and IMP can be changed. For example, an IMP can have multiple selectors pointing to it.
Method Swizzling can exchange two methods for implementation. In OC, there are two ways to extend the class. First, subclass. You can override a method to call the implementation of the parent class, which also means that you must use this subclass instance. However, if we use Category classification, after rewriting a method, we can no longer call the original method.
Method Swizzling can solve this problem. You can rewrite a Method without inheritance, and call the original implementation. The usual practice is to add a method to the Category, which can be implemented through the running method method_exchangeImplementations.