Runtime Runtimes: Methods and messages

Source: Internet
Author: User
Tags set set

Runtime Runtimes: Methods and messages

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 type Sel

The SEL, also called the selector, is a pointer to the selector that represents a method, which is defined as follows:

typedefstruct objc_selector *SEL;

The detailed definition of the bjc_selector struct is not

@selector(method1);NSLog(@"sel : %p"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 new selector at run time, or we can get the existing selector at run time, we can get the SEL in the following three ways:

Sel_registername function
@selector () provided by the Objective-c compiler
Nsselectorfromstring () method

IMP

The imp is actually a function pointer pointing to the first address of the method implementation. It is defined as follows:

...)

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.

Method

After introducing SEL and IMP, we can talk about method. method is used to represent methods in a class definition, define the following

typedef struct objc_method *Method;{    SEL method_name                 OBJC2_UNAVAILABLE;  // 方法名    char *method_types                  OBJC2_UNAVAILABLE;    IMP method_imp                      OBJC2_UNAVAILABLE;  // 方法实现}

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:

structchar *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.

Method

The 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 structurevoid Method_invoke_stret (ID receiver, Method m, ...);//Get method nameSEL Method_getname ( Method m );implementation of the//return methodIMP Method_getimplementation ( Method m );//Gets a string that describes the method parameter and the type of the return valueConstchar * method_gettypeencoding ( Method m );//Gets the string of the method's return value typechar * Method_copyreturntype ( Method m );//Gets the type string of the specified positional parameter of the methodchar * Method_copyargumenttype ( Method m, unsigned int index );//Return value type string by reference to return methodvoid Method_getreturntype ( Method m, Char *DST, size_t dst_len );//Returns the number of parameters for the methodunsigned int method_getnumberofarguments ( Method m );//Specify the type string of positional arguments by referencing the return methodvoid Method_getargumenttype ( Method m, unsigned int index, Char *DST, size_ T Dst_len );//Returns the method description structure of the specified methodstruct Objc_method_description * Method_getdescription ( Method m );//Implementation of the Setup methodIMP Method_setimplementation ( Method m, imp imp );//Exchange two implementation of methodsvoid Method_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 Selector

Selector-related action functions include:

// 返回给定选择器指定的方法的名称constchar * sel_getName ( SEL sel );// 在Objective-C Runtime系统中注册一个方法,将方法名映射到一个选择器,并返回这个选择器constchar *str );// 在Objective-C Runtime系统中注册一个方法constchar *str );// 比较两个选择器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 Process

In 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:

...)

This function completes all the things that are bound dynamically:

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.
It invokes the method implementation and passes the recipient object and all parameters of the method to it.
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:

Pointer to parent class
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.

Hide Parameters

Objc_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();    ifself || method == _cmd )        returnnil;    return [target performSelector:method];}

Of course, these two parameters we use more is self,_cmd in the actual use of relatively little.

Get method Address

The 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:

(*setter)(id, SEL, BOOL);int i;setter = (void (*)(id, SEL, BOOL))[target methodForSelector:@selector(setFilled:)];for01000 ; 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 forwarding

When 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:

method]:selectorto0to‘NSInvalidArgumentException‘‘-[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 Parsing

When 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<    /span> *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 recipient

If 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:

#pragma mark-Alternate recipient methods- (ID) Forwardingtargetforselector: (SEL) aselector{if(Aselector = =@selector(sendMessage:)) {return[Messageforwarding new]; }return Nil;}. h#import <Foundation/Foundation.h>  @interface messageforwarding : nsobject - (void) SendMessage: (NSString*) word;@end. M- (void) SendMessage: (NSString*) word{NSLog(@"Fast forwarding way:send message =%@", word);}

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 forwarding

If 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 at this time

- (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.

- (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 full instance code is as follows:

-(Nsmethodsignature *) Methodsignatureforselector: (SEL)  aselector{//Aselector can you get a method signature from this class  nsmethodsignature *methodsignature = [super  Methodsignatureforselector:aselector]; if  (!methodsignature) {methodsignature = [nsmethodsignature signaturewithobjctypes: "[email protected]:*"    ]; } return  methodsignature;} -(void ) Forwardinvocation: (nsinvocation *) aninvocation{messageforwarding *    messageforwarding = [messageforwarding new ]; if  ([messageforwarding RespondsToSelector:anInvocation.selector])    {[Aninvocation invokewithtarget:messageforwarding]; }}

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 inheritance

Looking 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 {        the aSelector message can     *         toandthat  *         toitifit can.  */    }    return NO;  }
Summary

Here, 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.

Reference: http://southpeak.github.io/blog/2014/11/03/objective-c-runtime-yun-xing-shi-zhi-san-:fang-fa-yu-xiao-xi-zhuan-fa/

Runtime Runtimes: 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.