Versions and platforms
The runtime system, for Objective-c, is like its operating system, or running support platform, which allows the OBJECTIVE-C code to run in accordance with established language features. In contrast to C + +, objective-c as much as possible to postpone some actions to run, that is, to do things as dynamically as possible. Therefore, it requires not only a compiler, but also a runtime environment to execute the compiled code.
The Runtime system is divided into two versions of legacy and modern, and generally, we now use the modern version. A notable feature of the modern version of the runtime system is "Non-fragile", where the child class does not need to be recompiled when the layout of the member variables of the parent class changes. In addition, compositing operations (that is, @property and @synthesis) for declared properties are also supported.
The following discusses how the NSObject class, the OBJECTIVE-C program interacts with the runtime system, dynamically loads classes at runtime, sends messages to other objects, and how the runtime obtains object information.
Interacting with the runtime system
The OBJECTIVE-C program interacts with the runtime system at three different levels: through Objective-c source code, through NSObject defined functions, and by calling runtime functions directly.
Normally, the Runtime system works behind the scenes, and all we need to do is write the objective-c code and compile it. The compiler will create the appropriate data structures and function calls for us to implement the dynamic nature of the language. These data structures hold information that can be found in class, category definitions, and protocol declarations, including member variable templates, selectors, and other information extracted from the source.
Runtime system is a dynamic shared library, located in/USR/INCLUDE/OBJC, which has a common set of interfaces, consisting of a series of functions and data structures. Developers can use pure C to call some functions to do what the compiler does, or to extend the runtime System, make some tools for the development environment, and so on. Although in general, writing objective-c does not need to know about these things, it can be useful in some cases. All functions have documented information in Objective-c Runtime reference.
Most of the objects in cocoa are subclasses of the NSObject (Nsproxy is an exception) and inherit the NSObject method. Thus, in this inheritance system, subclasses can re-implement some of the functions defined by the nsobject as needed to achieve polymorphism and dynamics, such as the description method (which returns a string describing itself, similar to the three quotation marks at the beginning of Python).
Some nsobject-defined methods simply ask the runtime system to obtain information, allowing the object to be self-introspective (introspection), such as Iskindofclass for determining the class type: Ismemberofclass to determine the position of an object in the inheritance system: Determine whether an object can receive a respondstoselector of a particular message:, determine whether an object follows the Conformstoprotocol of a protocol:, and provide methods to implement the address of the Methodforselector:. These methods allow an object to be introspective (introspect about itself).
The most important runtime function is to send a message, which is fired by the message expression in the source code. Sending a message is the most frequently occurring expression in a objective-c program, and the expression is eventually converted to a objc_msgsend function call. For example, a message expression [receiver message] will be converted to Objc_msgsend (receiver, selector), if the parameter is objc_msgsend (receiver, selector, arg1, arg2 , ...)。
The message is bound to the function implementation only at run time: First objc_msgsend finds the selector corresponding function implementation in receiver, and then invokes the Function procedure, passing the receiving object (that is, the this pointer) and the arguments to the past; Returns the return value of the function.
The key to sending messages is the structure that the compiler creates for classes and objects, contains two main elements, one pointer to superclass, and the other is the dispatch table of the class, which dispatch The table entries in the tables associate the selector with the corresponding function entry address.
When an object is created, the first element in the memory layout is a pointer to the class structure, ISA. With the ISA pointer, an object can access its class structure, which in turn accesses the inherited class structure. A sample diagram can be found here.
When sending a message to an object, Objc_msgsend looks through the ISA pointer in the class's dispatch table for the corresponding selector function entry address, if not found, along class hierarchy (the class's inheritance system), Until the NSObject class. This is the choice of a function implementation at run time, in the jargon of OOP, it is dynamic binding.
To speed up the sending of messages, the Runtime system creates a cache for each class that caches mappings for selector and corresponding function entry addresses.
When Objc_msgsend found the corresponding function implementation, it passed two hidden arguments in addition to the function arguments: receiving object and selector. This is called a hidden parameter because the two parameters are not declared in the source code, but can still be accessed through self and _cmd.
When a message is to be sent to an object many times, you can use methodforselector directly to optimize it, such as the following code:
//////////////////////////////////////////////////////////////
void (*setter)(id, SEL, BOOL);
int i;
setter = (void (*)(id, SEL, BOOL))[target
methodForSelector:@selector(setFilled:)];
for ( i = 0; i < 1000, i++ )
setter(targetList[i], @selector(setFilled:), YES);
//////////////////////////////////////////////////////////////
Among them, Methodforselector: is provided by the Cocoa Runtime system, not the language features of the objective-c itself. It is important to note the correctness of the function type in the conversion process, including the return value and parameters, and the first two parameters here need to be declared as IDs and SEL.
[3] Dynamic resolution of the method
Sometimes we want to provide implementations dynamically for a method, such as the @dynamic indicator of objective-c, which tells the compiler that the method that corresponds to the property is provided dynamically. We can use Resolveinstancemethod: and Resolveclassmethod: To provide dynamic implementations for object methods and class methods, respectively.
A objective-c method is essentially a C function with at least two parameters (self and _cmd), and we can use Class_addmethod to add a method to a class. For example, for the following function:
//////////////////////////////////////////////////////////////
void dynamicMethodIMP(id self, SEL _cmd) {
// implementation ….
}
//////////////////////////////////////////////////////////////
We can use Resolveinstancemethod: add it as a method (for example, called resolvethismethoddynamically):
//////////////////////////////////////////////////////////////
@implementation MyClass
+ (BOOL)resolveInstanceMethod:(SEL)aSEL
{
if (aSEL == @selector(resolveThisMethodDynamically)) {
class_addMethod([self class], aSEL, (IMP) dynamicMethodIMP, "v@:");
return YES;
}
return [super resolveInstanceMethod:aSEL];
}
@end
//////////////////////////////////////////////////////////////
Dynamic resolutions and sending messages do not conflict, a class is a method that has the opportunity to dynamically resolution before the message mechanism functions. When Respondstoselector: or instancesrespondtoselector: Activated, the dynamic method Resolver has an opportunity to provide an implementation for this selector. If Resolveinstancemethod is implemented: for selectors that do not want a dynamic resolution and want to follow the message forwarding mechanism, return No.
The OBJECTIVE-C program can link a new class and category at run time. Dynamic loading can be used to do a lot of different things, such as system preferences in which the various modules are dynamically loaded. Although there are run-time functions that dynamically load the Objective-c module (objc_loadmodules in objc/objc-load.h), Cocoa's NSBundle class provides a more convenient dynamic loading interface.
[4] Message forwarding
Sending a message to an object that it does not process is an error, but before the error is made, the Runtime system gives the receiving object a second chance to process the message. In this case, the Runtime system sends a message to the object, Forwardinvocation: The message carries only a Nsinvocation object as a parameter-the Nsinvocation object wraps the original message and the corresponding parameter.
By implementing the Forwardinvocation: Method (inherited from NSObject), you can give an unresponsive message a default treatment. As with the method name, the usual way to do this is to forward the message to another object:
//////////////////////////////////////////////////////////////
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
if ([someOtherObject respondsToSelector:[anInvocation selector]])
[anInvocation invokeWithTarget:someOtherObject];
else
[super forwardInvocation:anInvocation];
}
//////////////////////////////////////////////////////////////
For unrecognized messages (not found in dispatch table), Forwardinvocation: Like a broker, it is up to the developer to continue posting or to stop processing.
[5] Type encoding
To support the runtime System, the compiler encodes the return value type, the parameter type, and the corresponding compiler designator is @encode.
For example, the void code is V,CHAR encoded as C, the object encoding is @, the class is encoded as #, the selector is encoded as:, and the conforming type consists of the basic type, such as
typedef struct example {
id anObject;
char *aString;
int anInt;
} Example;
encoded as {[email protected]*i}.
[6] Property declaration
When the compiler encounters a property declaration, it generates some descriptive metadata (metadata) associated with the corresponding class, category, and protocol. There are some functions that can find these metadata by name in a class or protocol, through which we can get the encoded property type (string) and copy the attribute list of properties (C string array). Therefore, we can get the list of properties for each class and protocol.
Similar to type encoding, the attribute type also has a corresponding encoding scheme, such as readonly encoding for R,COPY encoding C,retain encoded as &, and so on.
The Property_getattributes function can be followed by the encoded string, which begins with T, immediately following the @encode type and comma, followed by the V and the variable name. Like what:
@property char charDefault;
Described as: Tc,vchardefault
@property(retain)ididRetain;
The description is: [Email protected],&,vidretain
The property struct defines an opaque handle to the attribute descriptor: typedef struct OBJC_PROPERTY *property;.
The corresponding array of attributes can be obtained through the class_copypropertylist and Protocol_copypropertylist functions:
objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount)
objc_property_t *protocol_copyPropertyList(Protocol *proto, unsigned int *outCount)
You can get the property name by using the Property_getname function.
by Class_getproperty and Protocol_getproperty, you can obtain a property reference based on the given name accordingly:
objc_property_t class_getProperty(Class cls, const char *name)
objc_property_t protocol_getProperty(Protocol *proto, const char *name, BOOL isRequiredProperty, BOOL isInstanceProperty)
The @encode type string for the property can be obtained through the Property_getattributes function:
const char *property_getAttributes(objc_property_t property)
The above functions are combined into a sample code:
Need to introduce libraries
LIBOBJC. A.dylib (Library) #import <objc/runtime.h> (head) |
@interface Lender : NSObject {
float alone;
}
@property float alone;
@end
id LenderClass = objc_getClass("Lender");
unsigned int outCount, i;
objc_property_t *properties = class_copyPropertyList(LenderClass, &outCount);
for (i = 0; i < outCount; i++) {
objc_property_t property = properties[i];
fprintf(stdout, "%s %s\n", property_getName(property), property_getAttributes(property));
}
——————————————————————————————————————————————————————————————————————————————————————————————————
Personal understanding
First, the compiler converts the code [obj makeText]; into objc_msgSend(obj, @selector (makeText)); in the objc_msgSend function. First find the class corresponding to obj through the isa pointer of obj. In the Class first go to the cache through SEL to find the corresponding function method (guess the cache in the method list is stored in the hash table with SEL as the key, which can improve the function search speed), if the cache is not found. Then look in the methodList, if not found in the methodlist, then look in the superClass. If it can be found, add the method to the cache to facilitate the next lookup, and jump to the corresponding function by the function pointer in the method.
When the method is executed, it is first found in the cache. If it is not found, it is found in the list of methods in this class. If it is not, it will be found in the parent class. If there is any, it will be added to the cache. If not, the error will be reported.
Is it handled when the cache is precompiled?
Objective-c's runtime System