We studied the method performs in previous articles, respectively, in the process of rapid flow, slow search process, the dynamic analytical but still there may be no deal with news, so today we’re going to explore the apple for the last opportunity to forward process, I didn’t handle the message, see if someone else can help me to deal with, please help!!
Fast forwarding process
I can’t handle the news. Let’s see if we can get some outside help
forwardingTargetForSelector
We declared a method instanceMethod in jPerson.h but didn’t implement it
@interface JPerson : NSObject
- (void)instanceMethod;
@end
Copy the code
Implement forwardingTargetForSelector method in JPerson. M fast forwarding
@implementation JPerson
-(id)forwardingTargetForSelector:(SEL)aSelector{
if (aSelector == @selector(instanceMethod)) {
JStudent *student = [[JStudent alloc] init];
if ([student respondsToSelector:aSelector]) {
return student;
}
}
return [super forwardingTargetForSelector:aSelector];
}
@end
Copy the code
InstanceMethod methods are implemented in jStudent.m
@implementation JStudent -(void)instanceMethod{NSLog(@"student method "); } @endCopy the code
In the main method
int main(int argc, const char * argv[]) {
@autoreleasepool {
JPerson *person = [JPerson alloc];
[person instanceMethod];
}
return 0;
}
Copy the code
We see that the message was successfully forwarded to the instance object of JStudent for processing
We see forwardingTargetForSelector this method is introduced
Returns the object to which unrecognized messages should first be directed
If the current object cannot process the message, it is passed to another object
If an object implements (or inherits) this method, and returns a non-
nil
(and non-self
) result, that returned object is used as the new receiver object and the message dispatch resumes to that new object. (Obviously if you returnself
from this method, the code would just fall into an infinite loop.)If an object implements this method and returns an object that is neither empty nor self, the object is processed as the new recipient of the message
Notice that if you return self then you’re going to create an infinite loop
If you implement this method in a non-root class, Invoking Super’s if your class has nothing to return for the given selector then you should return the result of invoking Super’s implementation.
If in the root class implements this method needs to return and there is no suitable object, so you need to perform [super forwardingTargetForSelector:] method
This method gives an object a chance to redirect an unknown message sent to it before the much more expensive
forwardInvocation:
machinery takes over. This is useful when you simply want to redirect messages to another object and can be an order of magnitude faster than regular forwarding. It is not useful where the goal of the forwarding is to capture the NSInvocation, or manipulate the arguments or return value during the forwarding.This means that the cost of forwarding a message is relatively low, and the cost of forwarding a message is much higher in the later slow forwarding process
In summary, this is a method that is responsible for forwarding messages, so be careful not to return self because it creates an infinite loop, so do super
Slow forwarding process
methodSignatureForSelector & forwardInvocation
Implement methodSignatureForSelector and forwardInvocation method in JPerson. M
@implementation JPerson
-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
if (aSelector == @selector(instanceMethod)) {
NSMethodSignature *signature = [NSMethodSignature signatureWithObjCTypes:"v@:@"];
return signature;
}
return [super methodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation{
JStudent *s = [JStudent alloc];
if ([s respondsToSelector:anInvocation.selector]) {
[anInvocation invokeWithTarget:s];
}else{
[super forwardInvocation:anInvocation];
}
}
@end
Copy the code
It also works
We can have a look at # # methodSignatureForSelector and forwardInvocation is introduced
Important
To respond to methods that your object does not itself recognize, you must override
methodSignatureForSelector:
in addition toforwardInvocation:
. The mechanism for forwarding messages uses information obtained frommethodSignatureForSelector:
to create theNSInvocation
object to be forwarded. Your overriding method must provide an appropriate method signature for the given selector, either by pre formulating one or by asking another object for one.
To respond to the current object itself can’t identify method, in addition to realize forwardInvocation: besides, you also have to rewrite methodSignatureForSelector:, forwarding message mechanism from methodSignatureForSelector of use: The information obtained to create the NSInvocation object to forward. Your override method must provide the appropriate method signature for a given selector, either by pre-specifying one or by asking another object for one.
This method is rarely used. From the previous introduction of fast forwarding, we can know that the cost of slow forwarding is much higher. This is the last chance given by Apple
Assembly validation
We can see this when we look at the stack information by bt name
frame #5: 0x00007fff20404fd6 CoreFoundation`___forwarding___ + 819
frame #6: 0x00007fff20404c18 CoreFoundation`_CF_forwarding_prep_0 + 120
......
Copy the code
Since the message forwarding process is in the CoreFoundation library, which is not yet open source, we took a peek at its flow through Hopper disassembly, dragging CoreFoundation into Hopper and searching for _forwarding_prep_0
Double-click to go to ____forwarding___
This is executedforwardingTargetForSelector
Method for fast forwarding process, if the fast forwarding process returnsnil
Just jump toloc_64a67
Determine whether realized methodSignatureForSelector method here, if there is no implementation is to jump to loc_64dd7
Finally performed doesNotRecognizeSelector error, if realized methodSignatureForSelector method will continue to execute _forwardStackInvocation – > forwardInvocation
whyresolveInstanceMethod
The method is executed twice
We found that resolveInstanceMethod was executed twice. Why?
The first call was made when the method could not be queried.
The second call is class_getInstanceMethod after the message forwarding process
class_getInstanceMethod
/*********************************************************************** * class_getInstanceMethod. Return the instance method for the * specified class and selector. **********************************************************************/ Method class_getInstanceMethod(Class cls, SEL sel) { if (! cls || ! sel) return nil; // This deliberately avoids +initialize because it historically did so. // This implementation is a bit weird because it's the only place that // wants a Method instead of an IMP. #warning fixme build and search caches // Search method lists, try method resolver, etc. lookUpImpOrForward(nil, sel, cls, LOOKUP_RESOLVER); #warning fixme build and search caches return _class_getMethod(cls, sel); }Copy the code
LookUpImpOrForward is executed and LOOKUP_RESOLVER is passed in as the behavior parameter
NEVER_INLINE IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior) { ...... If (slowpath(behavior & LOOKUP_RESOLVER)) {behavior ^= LOOKUP_RESOLVER; return resolveMethod_locked(inst, sel, cls, behavior); }... }Copy the code
In the forwarding process, the corresponding method may be added through the Runtime method, so if the message is not processed after the forwarding process, Apple goes through the query process again.
summary
If the message is still not processed after the search and dynamic parsing, Apple gives one last chance to forward the message. If the last chance is not grasped, it is a mistake.
Refer to the article
# objc_msgSend analysis – dynamic parsing + message forwarding