Objective
In the previous article we tasted the black magic of runtime, we can get the name of the member variable in the program compile stage, attribute and dynamic to the object increment attribute, and so on, in the next we further understand OC's message sending mechanism. If you have not had contact with the runtime before the classmate suggested to look at: the previous "Runtime play member variable"
OC's message sending mechanism is long heard, given that I have always felt that it is very low-level things need to spend a lot of time to learn to study it so has been a stir. Also do not do too much cushion, direct cut into it. When we use the OC object to invoke a method, such as this: [Lisi SayHello]; When the program runs, it is converted to the runtime code objc_msgnsend (Lisi, @selector (SayHello)), and by the literal meaning of the message sending function we can know that the message was sent SayHello to the Lisi object. The invocation of a method is actually sending a message to the class, and the same is true for the class method, which is actually an object and an instance of the Meta class. There are a number of functions in runtime that are similar to this message sending, including:
1 int *outcout) // get all the attribute list 2int *outcount) // Get an array of all methods 3 Bool Class_addmethod (class Cls,sel Name,imp IMP,constchar *type) // Add Method
Message forwarding Process:
When we create an instance variable and invoke an instance method, that is, [receiver message], convert to runtime code ID objc_msgsend (ID self,sel op ...), The corresponding OP is looked up first based on the ISA pointer of the instance to the list of methods in the specified class, if the corresponding OP is found, and if it is not found, it is searched in the corresponding parent class, so it loops up. All the way to the root class NSObject if it hasn't been found, it will be prioritized from high to low-key with the following three functions:
1 // The corresponding instance method is not obtained // the corresponding class method did not get to 2 - (ID) forwardingtargetforselector: (SEL) aselector3 -(void ) Forwardinvocation: (nsinvocation *) aninvocation
That is, when the class and its parent of an instance method are not implemented, the + Resolveinstancemethod: (SEL) SEL is called first, and if the method is not implemented then the call-(ID) Forwardingtargetforselector: (SEL) Aselector, a third-(void) Forwardinvocation: (Nsinvocation *) aninvocation is called if the second method is not implemented. If none of the three methods are implemented, the program throws an exception.
Note: The third method (void) Forwardinvocation: (nsinvocation *) aninvocation needs to be used in conjunction with Methodsignatureforselector for message forwarding. The purpose of Methodsignatureforselector is to create a valid signature for a method that is already implemented by a class.
The principle of message forwarding:
Each class has a list of method containing the SEL and the corresponding imp, that is, a method contains a SEL and a corresponding imp, and the message forwarding is to separate the original SEL from the IMP and regroup it with the other method.
The following is the implementation of message forwarding for runtime through a person class experience:
1, dynamic add function to implement message forwarding:
Person.h adds the following in this method and does not implement it in the person.m file:
-(void) goforwork;
To implement message forwarding in PERSON.M:
1+(BOOL) Resolveinstancemethod: (SEL) SEL2 {3NSString *selstring =nsstringfromselector (SEL);4 if([selstring isequaltostring:@"goforwork"]) {5 /**6 * Add a function implementation for a method that is not implemented in the class7 *8 * @param self class name9 * @param goforwork No way to implementTen * @IMP Workfunc added function implementation One * @ "[email protected]:" Type encoding of the typeencoding function type A * @return - */ -Class_addmethod (Self, @selector (goforwork), (IMP) Workfunc,"[email protected]:"); the } - return[Super Resolveinstancemethod:sel]; - } - + voidworkfunc (id self,sel SEL) - { +NSLog (@"Person go to work"); A}
The Goforwalk method of calling the person instance in the outside world can see the print table printing:
2, switch message recipients to implement message forwarding:
Giving a message to another object is also a form of message forwarding, usually by forwarding the message to other objects in the object, and it looks like the object is executing the method. We define a variable mydog of the dog type in the person class. Similarly, in a person who defines a walk method and does not implement it, the dog class also defines such a method and is implemented in implement.
Rewrite forwardingtargetforselector in person.m : Convert message recipient:
-(ID) forwardingtargetforselector: (SEL) aselector{ *selstring = nsstringfromselector (aselector); if ([selstring isequaltostring:@ "walk"]) { new]; return self.mydog; } return [Super Forwardingtargetforselector:aselector];}
After the outside call can be in the print console:
Convert Message object mode two:
methodsignatureforselector: And forwardinvocation: combine to implement message forwarding.
1-(Nsmethodsignature *) Methodsignatureforselector: (SEL) Aselector2 {3Nsmethodsignature *methodsignature =[Super Methodsignatureforselector:aselector];4 if(!methodsignature) {5Methodsignature =[Dog Instancemethodsignatureforselector:aselector];6 }7 returnmethodsignature;8 }9 Ten- (void) Forwardinvocation: (Nsinvocation *) Aninvocation One { A if([Dog instancesRespondToSelector:anInvocation.selector]) { - //Message Invocation - [Aninvocation InvokeWithTarget:self.myDog]; the - } -}
In this way, the same results can be printed on the print table.
The runtime method Exchange implements:
When I learned that the runtime was a soul-shifting dafa, I could not help but marvel at runtime Dafa, which is a dark magic and a bit of evil in mind.
Assuming there are two methods A and B, normally I send a message called the implementation of a, the B message is sent when the implementation of B is called. The so-called method Exchange implementation, that is, I send a message to the object is the implementation of B, the object is sent a B-message call is the implementation of a. Is this the legendary counter switch?
Runtime provides a series of functions to help us to cultivate the soul-shifting Dafa:
Method Class_getinstancemethod (class cls, SEL name)//get an instance methodIMP class_getmethodimplementation (class cls, SEL name)//get an implementation of a methodConst Char*method_gettypeencoding (method M)//gets the type encoding of a method typeImp Class_replacemethod (class CLS, SEL name, imp imp,Const Char*types)//Replace the implementation of another method with the implementation of one methodvoidMethod_exchangeimplementations (Method m1, method m2)//implementation of Exchange two methods
Thought probably with our dynamic Add method to realize the idea of the same, of course, you can also bully the bow, a apart directly on the two methods to exchange the implementation, but if there are two methods one of them did not achieve it??? What will you find??
The following two methods are defined in Person.h and the following implementations are made in the person.m file
-(void) drink{ NSLog (@ " I'm drinking ");} -(void) eat{ NSLog (@ " I'm eating ");}
In the initialization function we also "counter switch" the two ways:
1+ (person *) Personwithname: (NSString *) Name: (NSNumber *) Age Gender: (NSString *) gender clan: (NSString *) Clan2 {3Person *p = [personNew];4UnsignedintOutcount;5Ivar *ivararray = class_copyivarlist ([personclass], &outcount);6Object_setivar (P, ivararray[0], clan);7Object_setivar (P, ivararray[1], name);8Object_setivar (P, ivararray[2], gender);9Object_setivar (P, ivararray[3], age);Ten //The above is the runtime of the member variable operation, specifically can look at an article One Staticdispatch_once_t Oncetoken; ADispatch_once (&oncetoken, ^{ -Class Selfclass = [selfclass]; -SEL Asel =@selector (drink); theMethod Amethod =Class_getinstancemethod (Selfclass, Asel); -SEL Bsel =@selector (eat); -Method Bmethod =Class_getinstancemethod (Selfclass, Bsel); - //get the SEL pointer of two methods and the runtime method first +BOOL value =Class_addmethod (Selfclass, Asel, Method_getimplementation (Bmethod), method_gettypeencoding (BMethod)); - //Add the implementation of B to a body + if(value) { A Class_replacemethod (Selfclass, Bsel, Method_getimplementation (Amethod), method_gettypeencoding (AMethod)); /c0> at}Else{ - method_exchangeimplementations (Bmethod, amethod); - } - }); - returnp; -}
When the outside world sends drink messages to person objects, print the table:
Similarly, when sending eat messages, the print is "I'm eating". In general, two method exchange implementations are relatively rare in real-world requirements, especially in our custom methods, where it is easy to have unclear or even complex problems. Think about what a scheming boy would have done in the process of burying a bomb that wouldn't explode ... Good belly Black
However, if the counter switch application of good words in certain situations can save a lot of things, such as you want to update the version found material than the original name more than the same prefix, this time you can add a method for imagenamed to the name of all the material and the same prefix. This way, when the program calls Imagewithnamed, you will invoke your custom function implementation to easily update the name of the footage without manually adding a prefix to each search in the program. Or if you've just received a company project that needs to look at the project's framework all day long, you might be able to use all the Viewdidload plus a few logs to check it out.
First taste of the runtime, if there is any improper expression of the place also please point out. Follow-up will continue to update the runtime's learning.
Poke me download code!
Runtime message forwarding