In-depth introduction to cocoaOfMessage
Luo chaohui (Http://blog.csdn.net/kesalin)
Reprinted please indicate the source
In entry-level objc tutorials, we often say to programmers who transfer data from C ++, Java, or other object-oriented languages that method calls in objc (the term in objc is message) it is similar to method calls in other languages, but the form is somewhat different.
For example, in C ++:
Bird * abird = new bird (); Abird-> fly (); |
In objc:
Bird * abird = [[Bird alloc] init]; [Abird Fly]; |
At first glance, it seems that the writing format is different, but actually the difference is big. Method calls in C ++ may be dynamic or static, while messages in objc are dynamic. The following describes in detail why it is dynamic and what the compiler has done behind it.
To clarify the message topic, we must first understand three concepts: Class, Sel, and imp. They are defined in objc/objc. h:
Typedef struct objc_class * class; Typedef struct objc_object { Class ISA; } * ID; Typedef struct objc_selector * sel; Typedef ID (* IMP) (ID, Sel ,...); |
Meaning of class
Class is defined as a struct pointer to objc_class, which represents the class structure of each class. The objc_class is defined in objc/objc_class.h as follows:
Struct objc_class { Struct objc_class * ISA; Struct objc_class *Super_class ;/* Parent class */ Const char * Name;/* Class name */ Long version;/* Version information */ Long Info;/* Class information */ Long instance_size;/* Instance size */ Struct objc_ivar_list * ivars;/* Instance parameter linked list */ Struct objc_method_list ** methodlists;/* Method linked list */ Struct objc_cache * cache;/* Method cache */ Struct objc_protocol_list * protocols;/* Protocol linked list */ }; |
It can be seen that class is a pointer to a class struct, which contains a pointer to its parent class structure, a linked list of this class, cache of this class method and other necessary information.
The nsobject class method returns a pointer to its class structure. The first instance variable of each class instance object is a pointer to the class structure of the object, called Isa. With this pointer, the object can access its corresponding class and corresponding parent class. 1:
1. The first instance variable of the Instance Object represented by the circle is ISA, which points to the class structure of the object's class. This type of structure has a pointer superclass pointing to its parent class structure and a method linked list of its own message name (selector)/implementation address (address.
Meaning of the method:
Note that the method linked list stored here is of the method type. In Figure 1, selector refers to the sel of method, and address refers to the imp of method. Method is defined in the header file objc_class.h as follows:
Typedef struct objc_method * method; Typedef struct objc _ method { Sel method_name; Char * method_types; IMP method_imp; }; |
A method contains a method named sel-indicating the name of the method, a types-representing the type of the method parameter, and an imp-pointing to the specific implementation function pointer of the method.
Sel meaning:
As we can see earlier, the definition of method selection SEL is:
Typedef struct objc_selector * sel;
It is a pointer to objc_selector, indicating the method name/signature. Print the selector as follows.
-(Nsinteger) MAXIN :( nsinteger) A theother :( nsinteger) B { Return (A> B )? A: B; } Nslog (@ "sel = % s", @ selector (MAXIN: theother :)); Output: sel = MAXIN: theother: |
Different classes can have the same selector. This is no problem, because when the instance objects of different classes have the same selector, the message selector/address) the method chain table searches for specific methods to implement imp Based on selector, and then uses this method to implement specific implementation code. This is a process of dynamic binding. during compilation, we don't know which code will be executed in the end. We only query it through selector during execution, we can determine the specific Execution Code.
Meaning of IMP:
We can also see that IMP is defined:
Typedef ID (* IMP) (ID, Sel ,...);
According to the previous definition of ID, we know that ID is a pointer to the objc_object struct. This struct has only one ISA member, so any Class Object inherited from nsobject can be referred to by ID, because the first member instance of nsobject is ISA.
So far, we have a clear understanding of the meaning of IMP: IMP is a function pointer, the function to be pointed to contains a message receiving Object ID (Self pointer ), the optional sel (method name) of the call method, and the method parameters with an indefinite number, and an ID is returned. That is to say, IMP is the Execution Code of the final call of the message and the real implementation code of the method. We can use this function pointer like in C.
Methodforselector in the nsobject class: The method is used to obtain the pointer pointing to the method to implement imp. methodforselector: The returned pointer must be of the same type as the assigned variable, including the parameter type and Return Value Type of the method.
The following example shows how to use a pointer to call setfilled:
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 ); |
Using methodforselector: To avoid dynamic binding will reduce the overhead of most messages, but this is meaningful only when the specified message is repeatedly sent many times, such as the preceding for loop.
Note: methodforselector is a function provided by the cocoa runtime system, rather than the objective-C function.
Message calling process:
Now we have a general idea for the messages in objc: Example
Bird * abird = [[Bird alloc] init]; [Abird Fly]; |
For the fly call, the compiler inserts some code to convert it into calling the method to implement imp, this imp is found by finding the specific method corresponding to the selector with the fly name in the method linked list of the bird class structure.
The above ideas have some unmentioned topics, such as the code inserted by the compiler. What if the corresponding imp is not found in the method linked list? These topics will be discussed below.
Message function obj_msgsend:
The compiler converts a message to a call to the message function objc_msgsend. This function has two main parameters: the ID of the message receiver and the method selection sel of the message. At the same time, it receives any parameters in the message:
Id objc_msgsend (ID thereceiver, seltheselector ,...)
The above message [abird Fly] will be converted to function calls in the following form:
Objc_msgsend (abird, @ selector (FLY ));
The message function performs all the work required for dynamic binding:
1. It first finds the method corresponding to sel to implement imp. Because different classes may have different implementations for the same method, the method implementation depends on the type of the message receiver.
2. Then, the Message Receiver object (pointer to the Message Receiver object) and the parameters specified in the method are passed to the method to implement imp.
3. Finally, return the return value of the method implementation as the return value of the function.
The compiler will automatically Insert the code that calls the message function objc_msgsend. We do not need to display and call the message function in the code. When objc_msgsend finds the implementation of the method, it will directly call this method implementation, and pass all the parameters in the message to the method implementation. At the same time, it will also pass two hidden parameters: the receiver of the message and the method name SEL. These parameter help methods obtain information about message expressions. They are considered to be "hidden" because they are not declared in the source code of the method definition, but are implemented during code compilation.
Although these parameters are not explicitly declared, they can still be referenced in the source code (just as the instance variables of the message recipient object can be referenced ). In the method, you can use self to reference the Message Receiver object and use the optional _ cmd to reference the method itself. In the following example, _ cmd refers to the strange method, and Self refers to the object that receives the strange message.
-Strange { Id target = getthereceiver (); Sel method = getthemethod (); If (target = self | mothod = _ cmd) Return nil; Return [target parameter mselector: method]; } |
Among the two parameters, self is more useful. In fact, it is a way to access the instance variable of the Message Receiver object in method implementation.
Procedure of searching imp:
As mentioned above, objc_msgsend will find the method implementation imp in the method list of the class structure based on the method selection SEL. Here are some articles in the header. We also see a member named objc_cache * in the previous class structure. This cache exists to improve efficiency. Each class has an independent cache, including both the inherited methods and the methods defined in the class ..
The following is an analysis of Apple's official runtime source code:
static Method look_up_method(Class cls, SEL sel, BOOL withCache, BOOL withResolver){ Method meth = NULL; if (withCache) { meth = _cache_getMethod(cls, sel, &_objc_msgForward_internal); if (meth == (Method)1) { // Cache contains forward:: . Stop searching. return NULL; } } if (!meth) meth = _class_getMethod(cls, sel); if (!meth && withResolver) meth = _class_resolveMethod(cls, sel); return meth;}
By analyzing the code above, we can see that when searching:
1. First, search for the method cache of this class. If yes, return it;
2. If not found, go to the method list of the class. If it is found in the method list of this class, IMP is returned and added to the cache. According to the recent usage principle, this method is highly likely to be called again, and the cache can save the overhead of the next call and re-query. 3, 3. If the corresponding method is not found in the method list of this class
IMP, Which is searched in the method list of its parent class structure through the super_class pointer in this class structure until the corresponding imp is found in the method list of a parent class, and returns it, and add it to the cache;
4. If the corresponding imp is not found in the method list of itself and all parent classes, check whether dynamic method resolution can be performed (this topic will be described later );
5. If the dynamic method resolution fails to solve the problem, go to the Message forwarding process described below.
Convenience functions:
We can use some nsobject methods to obtain the runtime information or dynamically execute some messages:
Class returns the class of the object;
Iskindofclass and ismemberofclass check whether the object is in the specified class inheritance system;
Respondstoselector checks whether the object can specify a message;
Conformstoprotocol checks whether the object implements the method of the specified Protocol Class;
Methodforselector returns the address of the specified method implementation.
Performselector: withobject indicates the method used to execute SEL.
Message forwarding:
Generally, an error message is prompted when you send messages to an object that cannot be processed. However, before the objective-C runtime system throws an error, A special message forwardinvocation is sent to the message receiving object to notify the object. The unique parameter of the message is an nsinvocation object, which encapsulates the parameters of the original message and message.
We can implement forwardinvocation: The method to perform some default processing for messages that cannot be processed, or to forward messages to other objects for processing without throwing an error.
For the role of message forwarding, consider the following scenarios: Suppose we need to design an object that can respond to the negotiate message and include responses of other types of objects to the message. Through the implementation of the negotiate method, it is easy to forward the negotiate message to other objects.
Furthermore, let's assume that we want our object to be exactly the same as the response of another class object to the negotiate message. One possible way is to let our class inherit the implementation of methods of other classes. Then, sometimes this method is not feasible, because our class and other classes may need to respond to the negotiate message in different inheritance systems.
Although our class cannot inherit the negotiate method of other classes, we can still provide a method implementation, which simply forwards the negotiate message to other class objects, it's like "borrowing" from other classes. As follows:
-Negotiate { If ([someotherobject respondstoselector: @ selector (negotiate)]) Return [someotherobject negotiate]; Return self; } |
This method is not flexible, especially when many messages want to be transmitted to other objects, we must provide methods for each message. In addition, this method cannot process unknown messages. When we write the code, all the sets of messages we need to forward must be determined. However, in fact, this set will change with the occurrence of running events, the definition of new methods or classes.
Forwardinvocation: a message provides a more special and dynamic solution to this problem: when an object cannot respond to a message because it does not have a corresponding implementation method, at runtime, the system will send a message to this object through forwardinvocation. Each object inherits the forwardinvocation: method from the nsobject class. However, the method implementation in nsobject simply calls doesnotrecognizeselector :. By implementing our own forwardinvocation: method, we can forward messages to other objects in this method.
To forward messages to other objects, forwardinvocation: The methods must do the following:
1. decide who to forward the message to and
2. Forward the message with the original parameter.
Messages can be forwarded using the invokewithtarget: method:
-(Void) forwardinvocation :( nsinvocation *) aninvocation { If ([someotherobject respondstoselector: [aninvocation selector]) [Aninvocation invokewithtarget: someotherobject]; Else [Super forwardinvocation: aninvocation]; } |
The returned value after the message is forwarded is returned to the original message sender. You can return any type of return values, including ID, struct, and floating point.
Forwardinvocation: A method is like a distribution center for unrecognized messages and forwards these messages to different recipients. Or it can send all messages to the same receiving object as a transport station. It can translate a message into another message, or simply "eat" some messages, so there is no response or error. Forwardinvocation: The method can also provide the same response to different messages, depending on the specific implementation of the method. This method provides the ability to link different objects to the message chain.
Note:Forwardinvocation: The method is called only when the message receiving object cannot respond to the Message normally. Therefore, if we want an object to forward the negotiate message to other objects, this object cannot have the negotiate method or be implemented in the dynamic method resolution process. Otherwise, forwardinvocation will not be called.
References:
Objective-cruntime reference:
Http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ObjCRuntimeRef/Reference/reference.html
Objective-C runtime programming guide:
Http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Introduction/Introduction.html