[OBJECTC] Runtime run-time three: methods and messages

Source: Internet
Author: User

in this chapter, we're going to start talking about the most interesting part of runtime: the message handling mechanism. we will discuss the sending of messages and the forwarding of messages in detail.

underlying data typeSELThe sel, also called the selector, is a pointer to the selector that represents a method , which is defined as follows:
typedef struct OBJC_SELECTOR *sel;
The detailed definition of the objc_selector struct is not found in the <objc/runtime.h> header file. The selector method is used to represent the name of the run-time method. objective-c at compile time, a unique integer identifier (an int type address) is generated based on the name and parameter sequence of each method, which is the SEL. As shown in the following code:
SEL Sel1 = @selector (METHOD1); NSLog (@ "SEL:%p", Sel1);
The above output is:
2014-10-30 18:40:07.518 runtimetest[52734:466626] sel:0x100002d72
between two classes, regardless of whether they are the parent class's relationship to the subclass, or if there is no such relationship, the sel of the method is the same as long as the method name is the same. Each method corresponds to an sel. so in objective-c the same class (and class inheritance system), there cannot be 2 methods with the same name, even if the parameter types are different. The same method can only correspond to one sel. This also results in a poor ability for objective-c to handle methods with the same method name and with the same number of arguments but different types. As in a class, define the following two methods:
-(void) SetWidth: (int) width;-(void) SetWidth: (double) width;
Such a definition is considered a compilation error, so we can't do it like C + +, C #. Instead, it needs to be declared as follows:
-(void) Setwidthintvalue: (int) width;-(void) Setwidthdoublevalue: (double) width;
Of course, different classes can have the same selector, which is fine. When instance objects of different classes perform the same selector, they will find their corresponding imp in the respective list of methods according to selector.
All the SEL in the project consists of a set set, which is unique, so the SEL is unique. So, if we think of this method in the collection to find a method, we just need to find the corresponding sel of the method, the SEL is actually a hash of the method name of a string, and for the comparison of strings only need to compare their address on it, you can say the speed of the incomparable!! However, there is a problem, that is, the increase in number will increase the hash conflict caused by performance degradation (or no conflict, because it may also be used perfect hash). However, regardless of the method used to accelerate, if the total amount can be reduced (multiple methods may correspond to the same SEL), it will be the most sharp method. So, it's not hard to understand why SEL is just a function name.
In essence, the SEL is just a pointer to a method (to be exact, a key value that is hashed based on the method name, which uniquely represents a method), and it exists only to speed up the query of the method. This look-up process will be discussed below.
We can Add a new selector at run time, or you can get an existing selector at run time, we have the following three ways to get the SEL:
    1. Sel_registername function
    2. @selector () provided by the Objective-c compiler
    3. Nsselectorfromstring () method
IMPThe imp is actually a function pointer pointing to the first address of the method implementation. It is defined as follows:
ID (*IMP) (ID, SEL, ...)
This function uses the standard C calling convention implemented by the current CPU architecture. The first argument is a pointer to self (the memory address of the class instance if it is an instance method, or a pointer to a meta class if it is a class method), the second parameter is the method selector (selector), followed by the actual argument list of the method.
The SEL described earlier is to find the final implementation of the method imp. Since each method corresponds to a unique sel, we can quickly and accurately obtain the imp corresponding to it using the SEL, and the lookup process will be discussed below. When we get the imp, we have the entry point to execute this method code, so we can use the function pointer just like the normal C language function.
by acquiring IMP, we can skip the message passing mechanism of runtime and directly execute the function implemented by IMP, thus eliminating the series of lookups performed during runtime messaging, which is more efficient than sending messages directly to the object.
MethodAfter introducing SEL and IMP, we can talk about method. method is used to represent methods in a class definition, it is defined as follows:
typedef struct Objc_method *method;struct objc_method {SEL method_name objc2_unavailable;//Method name char *method_types OBJC2_ Unavailable;imp Method_imp objc2_unavailable; Method implementation}
We can see that the struct contains a SEL and imp, which is actually a mapping between the SEL and the Imp. With SEL, we can find the corresponding IMP, thus invoking the implementation code of the method. We will discuss the specific operating procedures below.
Objc_method_description
Objc_method_description defines a objective-c method, which is defined as follows:
struct Objc_method_description {SEL name; char *types;};
method related Operation function Runtime provides a series of methods to handle the operations associated with the method. Includes the method itself and the SEL. in this section we describe these functions.
Methodthe method operation related functions include the following:
Call the implementation ID of the specified method Method_invoke (ID receiver, method m, ...); /Call the implementation of a method that returns a data structure void Method_invoke_stret (ID receiver, method m, ...);  /Get Method Name Sel Method_getname (method m);//The implementation of the Return method Imp Method_getimplementation (method m);//Gets the string that describes the method parameter and the return value type const CHAR  * Method_gettypeencoding (method m);//Gets the string of the return value type of the method char * Method_copyreturntype (method m);//Gets the type string of the specified positional parameter of the method char  * Method_copyargumenttype (method m, unsigned int index),//The return value type string of the return method by reference, void Method_getreturntype (method M, Char *DST, size_t Dst_len);//Returns the number of arguments to the method unsigned int method_getnumberofarguments (method m);//type string specifying positional parameters by reference return method void met Hod_getargumenttype (method m, unsigned int index, char *dst, size_t dst_len);//Returns a method that specifies the method description struct struct OBJC_METHOD_DESCRI Ption * Method_getdescription (method m);//Implementation of the Setup method IMP Method_setimplementation (method M, imp imp);//Exchange two methods implementation void M Ethod_exchangeimplementations (Method m1, method m2);
    • The Method_invoke function returns the actual implementation of the return value. The parameter receiver cannot be empty. This method will be more efficient than method_getimplementation and method_getname.
    • The Method_getname function returns a sel. If you want to get the C string for the method name, you can use Sel_getname (Method_getname (method)).
    • Method_getreturntype function, the type string is copied into DST.
    • Method_setimplementation function, note that the function return value is the implementation before the method.
Method SelectorSelector-related action functions include:
Returns the name of the method specified by the given selector const char * SEL_GETNAME (SEL sel);//Registers a method in the Objective-c runtime system, maps the method name to a selector, and returns the selector sel Sel_regi  Stername (const char *STR);//Register a method in the Objective-c runtime system sel sel_getuid (const char *STR);//Compare two selectors bool Sel_isequal (Sel LHS, sel RHS);
Sel_registername function: When we add a method to the class definition, we must register a method name in the OBJECTIVE-C runtime system to get the selector of the method.
Method Invocation ProcessIn Objective-c, the message is not bound to the method implementation until run time. The compiler translates the message expression [receiver message] into a call to a message function, which is objc_msgsend. This function takes the message receiver and the method name as its underlying parameters, as shown in the following:
Objc_msgsend (receiver, selector)
If there are other parameters in the message, the form of the method is as follows:
Objc_msgsend (receiver, selector, arg1, arg2, ...)
This function completes all the things that are bound dynamically:
    1. First it finds the selector corresponding to the implementation of the method. Because the same method may have different implementations in different classes, we need to rely on the recipient's class to find the exact implementation.
    2. It invokes the method implementation and passes the recipient object and all parameters of the method to it.
    3. Finally, it will implement the returned value as its own return value.
The key to the message is the struct objc_class we discussed in the previous section, which has two fields that we are interested in distributing the message:
    1. Pointer to parent class
    2. A method of a class is published, that is, methodlists.
When we create a new object, we first allocate memory for it and initialize its member variables. Where the ISA pointer is also initialized, allowing the object to access the class and class inheritance system. Demonstrates the basic framework for such a message:

When a message is sent to an object, Objc_msgsend gets to the struct of the class through the object's Isa pointer, and then finds the selector of the method in the method sub-publication. If selector is not found, the parent class is found through a pointer to the parent class in the Objc_msgsend struct, and the selector of the method is looked up in the sub-publication of the parent class. In this way, the NSObject class will be reached along the inheritance system of the class. Once positioned to selector, the function acquires the implementation entry point and passes in the corresponding parameters to execute the method's implementation. If the selector is not located at the end, it will go through the message forwarding process, which we'll discuss later.
To speed up the processing of messages, the runtime system caches the addresses of used selector and corresponding methods. We have discussed this point before and we will not repeat it.
Hide ParametersObjc_msgsend has two hidden parameters:
    1. Message Receive Object
    2. Selector of the method
These two parameters provide the caller's information for the implementation of the method. The reason is that they are hidden because they are not declared in the source code of the defined method. They are inserted into the implementation code at compile time.
Although these parameters do not display declarations, they can still be referenced in your code. We can use self to refer to the recipient object and use _cmd to refer to the selector. As shown in the following code:
-Strange{id target = Getthereceiver (); SEL method = Getthemethod (); if (target = = Self | | method = = _cmd) return Nil;return [target Performselector:method];}
Of course, these two parameters we use more is self,_cmd in the actual use of relatively little.
Get method Addressthe dynamic binding of methods in runtime allows us to write code more flexibly, such as we can forward the message to the object we want, or arbitrarily swap the implementation of a method. But the increased flexibility also leads to some performance losses. After all, we need to find the implementation of the method, not as straightforward as the function call. Of course, the caching of methods solves this problem to a certain extent.
As we mentioned above, if we want to avoid this dynamic binding, we can get the address of the method implementation, and then call it directly like a function call. This can improve the performance of the program, especially when we need to invoke a particular method frequently within a loop.
The NSObject class provides a methodforselector: method that allows us to obtain a pointer to a method and then invoke the implementation code through this pointer. We need to convert the Methodforselector: The returned pointer to the appropriate function type, and the function arguments and return values need to be matched.
Let's take a look at Methodforselector: using the following code:
void (*setter) (ID, SEL, BOOL), int i;setter = (void (*) (ID, SEL, BOOL)) [Targetmethodforselector: @selector (setfilled:)]; for (i = 0; i < i++) setter (Targetlist[i], @selector (setfilled:), YES);
It is important to note that the first two parameters of a function pointer must be ID and sel.
This approach, of course, is only suitable for situations where the same method is frequently called in a case similar to a for loop to improve performance. In addition, Methodforselector: is provided by the Cocoa runtime; it is not a feature of the Objective-c language.
Message Forwardingwhen an object can receive a message, it goes through the normal method invocation process. But what happens if an object cannot receive the specified message? By default, if the method is invoked as an [object message], the compiler will error if object cannot respond to message messages. But if it's a perform ... , you need to wait until the runtime to determine if object can receive a message. If not, the program crashes.
In general, when we are not sure whether an object can receive a message, Respondstoselector is called first: to judge. As shown in the following code:
if ([Self Respondstoselector: @selector (method)]) {[Self Performselector: @selector (method)];}
However, we would like to discuss the situation of not using respondstoselector: judgment. This is the focus of our section.
When an object is unable to receive a message, it initiates the so-called "message forwarding" mechanism, through which we can tell the object how to handle an unknown message. By default, the object receives an unknown message that causes the program to crash, and through the console we can see the following exception information:
-[sutruntimemethod method]: Unrecognized selector sent to instance 0x100111940*** terminating apps due to uncaught exceptio N ' nsinvalidargumentexception ', Reason: '-[sutruntimemethod method]: Unrecognized selector sent to instance 0x100111940 '
This exception message is actually thrown by NSObject's "Doesnotrecognizeselector" method. However, there are some steps we can take to get our program to execute specific logic and avoid a program crash.
The message forwarding mechanism is basically divided into three steps:
    1. Dynamic Method Parsing
    2. Alternate recipient
    3. Full forwarding
Let's take a look at these three steps in detail.
Dynamic Method Parsingwhen an object receives an unknown message, it first calls the class method of the owning class +resolveinstancemethod: (instance method) or +resolveclassmethod: (class method). In this method, we have the opportunity to add a "processing method" to the unknown message. However, the premise of using this method is that we have implemented the "processing method", which can be dynamically added to the class by the Class_addmethod function at runtime. As shown in the following code:
void FunctionForMethod1 (id self, SEL _cmd) {NSLog (@ '%@,%p ', self, _cmd);} + (BOOL) Resolveinstancemethod: (SEL) sel {nsstring *selectorstring = nsstringfromselector (SEL); if ([Selectorstring isequaltostring:@ "Method1"]) {Class_addmethod (self. class, @selector (Method1), (IMP) FunctionForMethod1, "@:");} return [Super Resolveinstancemethod:sel];}
However, this scheme is more to implement the @dynamic attribute.
Alternate recipientIf the message cannot be processed in the previous step, runtime continues to tune the following methods:
-(ID) Forwardingtargetforselector: (SEL) Aselector
If an object implements this method and returns a non-nil result, the object will be the new recipient of the message, and the message will be distributed to the object. Of course, this object cannot be self itself, otherwise there is an infinite loop. Of course, if we don't specify the appropriate object to handle aselector, we should call the implementation of the parent class to return the result.
Using this method is usually within the object, and there may be a series of other objects that can handle the message, and we can borrow the object to process the message and return it, so that it is handled by the object in person outside the object. As shown in the following code:
@interface sutruntimemethodhelper:nsobject-(void) method2; @end @implementation sutruntimemethodhelper-(void) METHOD2 {NSLog (@ "%@,%p", Self, _cmd);} @end #pragma mark-@interface Sutruntimemethod () {Sutruntimemethodhelper *_helper;} @end @implementation sutruntimemethod+ (instancetype) object {return [[Self alloc] init];} -(Instancetype) init {self = [super init];if (self! = nil) {_helper = [[Sutruntimemethodhelper alloc] init];} return self;} -(void) test {[Self performselector: @selector (METHOD2)];} -(ID) Forwardingtargetforselector: (SEL) Aselector {NSLog (@ "Forwardingtargetforselector"); NSString *selectorstring = Nsstringfromselector (aselector);//forwarding messages to _helper to process if ([selectorstring isequaltostring:@] Method2 "]) {return _helper;} return [Super Forwardingtargetforselector:aselector];} @end
This step is appropriate for us to forward the message to another object that can handle the message. However, this step cannot process the message, such as the parameters and return values of the action message.
Full Message forwardingIf the unknown message cannot be processed in the previous step, the only thing that can be done is to enable the full message forwarding mechanism. The following methods are called:
-(void) Forwardinvocation: (nsinvocation *) aninvocation
At this stage, the runtime system will give the message recipient the last chance to forward the message to another object. The Nsinvocation object creates an object that represents the message, encapsulating all the details related to the message that has not been processed in aninvocation, including selector, target, and parameters. We can choose to forward the message to other objects in the Forwardinvocation method.
Forwardinvocation: The implementation of the method has two tasks:
Locates an object that can respond to messages encapsulated in Aninvocation. This object does not need to be able to handle all unknown messages.
Use Aninvocation as a parameter to send a message to the selected object. Aninvocation will retain the result of the call, and the runtime will fetch the result and send it to the original sender of the message.
However, in this approach we can implement some more complex functions, we can modify the content of the message, such as the recovery of a parameter, and then to trigger the message. In addition, if you find that a message should not be handled by this class, you should call the parent class with the same name so that each class in the inheritance system has an opportunity to handle this call request.

There is also a very important question that we have to rewrite in the following ways:
-(Nsmethodsignature *) Methodsignatureforselector: (SEL) Aselector

The message forwarding mechanism uses the information obtained from this method to create a Nsinvocation object. So we have to rewrite this method to provide a proper method signature for a given selector. The complete example is shown below:

-(Nsmethodsignature *) Methodsignatureforselector: (SEL) aselector {    nsmethodsignature *signature = [super Methodsignatureforselector:aselector];    if (!signature) {        if ([Sutruntimemethodhelper Instancesrespondtoselector:aselector]) {            signature = [ Sutruntimemethodhelper instancemethodsignatureforselector:aselector];        }    }    return signature;} -(void) Forwardinvocation: (nsinvocation *) aninvocation {    if ([Sutruntimemethodhelper instancesrespondtoselector : Aninvocation.selector]) {        [aninvocation invokewithtarget:_helper];}    }

NSObject's Forwardinvocation: The method implementation simply calls the Doesnotrecognizeselector: method, which does not forward any messages. Thus, an exception is thrown if the unknown message is not processed in the three steps described above.
In a sense, forwardinvocation: Like the distribution center of an unknown message, these unknown messages are forwarded to other objects. Or you can send all unknown messages to the same receiving object like a transport station. This depends on the specific implementation.
message forwarding and multiple inheritancelooking back at the second and third steps, we can allow an object to establish a relationship with other objects to handle some unknown message, while on the surface it is still the object that is processing the message. With this relationship, we can simulate some of the features of multiple inheritance, allowing objects to "inherit" the attributes of other objects to handle things. However, there is an important difference between the two: multiple inheritance integrates different functions into an object, which causes the object to become too large and involves too much, while message forwarding decomposes the functionality into separate, small objects and somehow connects them and makes the appropriate message forwarding.

However, although message forwarding is similar to inheritance, some methods of nsobject can distinguish between the two. such as Respondstoselector: and Iskindofclass: can only be used for the inheritance system, but not for the forwarding chain. If we want this message forwarding to look like an inheritance, you can override these methods, as shown in the following code:

(BOOL) Respondstoselector: (SEL) aselector   {       if ([Super Respondstoselector:aselector]) return YES;     else {/          * here, test whether the aselector message can * * is     forwarded to            another object and whether that
   
    *           * Object can respond to it. Return YES if it can.  */     }        return NO;  
   

Summaryhere, we have learned the basic mechanism of message sending and forwarding in runtime. This is also the power of runtime, through which we can add a lot of dynamic behavior to the program, although we rarely use these mechanisms directly in real-world development (such as calling Objc_msgsend directly), but understanding them helps us to learn more about the underlying implementations. In fact, in the actual coding process, we also have the flexibility to use these mechanisms to achieve some special functions, such as hook operation.

[OBJECTC] Runtime run-time three: methods and messages

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.