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 return self 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 to forwardInvocation:. The mechanism for forwarding messages uses information obtained from methodSignatureForSelector: to create the NSInvocation 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 executedforwardingTargetForSelectorMethod for fast forwarding process, if the fast forwarding process returnsnilJust 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

whyresolveInstanceMethodThe 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