The message forwarding mechanism of OC is a question most interviewers often ask during the interview process. Here I sort out my understanding of the OC message forwarding mechanism.

As we all know, when an object of OC sends a message, it will first search in the struct objc_method_list list of the class. If it finds a method, it will directly call the implementation of the related method. If not, it will search up the inheritance tree through the super_class pointer. If you get to the root of the inheritance tree (usually NSObject) and you haven’t found it yet. A method that is called NSObjec doesNotRecognizeSelector: and this will quote unrecognized selector that error. Before actually doesNotRecognizeSelector: the call method will forward message three times – and opportunity to remedy. Also known as the three remedies of OC message forwarding.

In general, an OC message is sent through four stages (each of which searches for NSObject before moving on to the next stage)

1) First search for the implementation of the modified method in this class, call it directly if there is one, search the parent class until NSObject, if NSObject does not go to message forwarding (dynamic method resolution of the class, alternate recipient object, complete message forwarding).

2) Class dynamic method parsing:

I’m going to create my SonPerson class, and I’m going to write it in my ViewController

id person = [[SonPerson alloc]init];
[person appendString:@""];
Copy the code

Note that we need to use id here otherwise we’ll get an error.

The +(BOOL)resolveClassMethod (SEL) SEL or +(BOOL)resolveInstanceMethod (SEL) SEL method is executed if no implementation of the method is found in the class or its parent. The +(BOOL)resolveClassMethod:(SEL) SEL or +(BOOL)resolveInstanceMethod:(SEL) SEL method is implemented using the black magic runtime dynamic addition method.

void dynamicAdditionMethodIMP(id self,SEL _cmd){
    NSLog(@"dynamicAdditionMethodIMP");
}

+(BOOL)resolveClassMethod:(SEL)sel{

    NSLog(@"resolveInstanceMethod: %@", NSStringFromSelector(sel));

    if(sel ==@selector(appendString:)) {

        class_addMethod([selfclass], sel, (IMP)dynamicAdditionMethodIMP,"v@:");

        returnYES;

    }

    return[superresolveClassMethod:sel];

}

+(BOOL)resolveInstanceMethod:(SEL)sel{

    NSLog(@"resolveInstanceMethod: %@", NSStringFromSelector(sel));

    if(sel ==@selector(appendString:)) {

        class_addMethod([selfclass], sel, (IMP)dynamicAdditionMethodIMP,"v@:");

        returnYES;

    }

    return[super resolveInstanceMethod:sel];

}

BOOL class_addMethod(Class cls, SEL name, IMP imp,constchar*types);
Copy the code

The first argument is the class to which the method is to be added, the second argument is a selector, which is the name of the instance method, and the third argument is a variable of type IMP which is the implementation of the function, and you need to pass in a C function that takes at least two arguments, id self and SEL _cmd, The fourth argument is the function type. For details, see the comments.

Console output:

resolveInstanceMethod: appendString:

dynamicAdditionMethodIMP

3) Standby receiver: Enter the standby receiver stage when +(BOOL)resolveClassMethod:(SEL) SEL or +(BOOL)resolveInstanceMethod:(SEL) SEL returns NO.

Create an alternate recipient class ForwardPerson to implement the appendString: method

-(void)appendString:(NSString*)str{

  NSLog(@"% @ = = = % @",NSStringFromClass([self class]),NSStringFromSelector(_cmd));

}
Copy the code

Implementation – (id) in the SonPerson class forwardingTargetForSelector: (SEL) aSelector method and returns a spare the receiver object

- (id)forwardingTargetForSelector:(SEL)aSelector{

   NSLog(@"forwardingTargetForSelector");

   return [ForwardPerson new];

}
Copy the code

Console output:

forwardingTargetForSelector

ForwardPerson===appendString:

4) Complete message forwarding: When -(void)forwardInvocation (NSInvocation*)anInvocation method nil, the message forwarding will enter the last stage, complete message forwarding. You also need to create a ForwardInvocation object

#import "ForwardInvocation.h"

@implementationForwardInvocation

-(void)appendString:(NSString*)str{

    NSLog(@"% @ = = = % @",NSStringFromClass([self class]),NSStringFromSelector(_cmd));

}

@end
Copy the code

Implement -(void)for onwards invocation in SonPerson :(NSInvocation*)anInvocation and – (NSMethodSignature *) methodSignatureForSelector (SEL) aSelector method

-(void)forwardInvocation:(NSInvocation*)anInvocation{

    NSLog(@"forwardInvocation");

    if([ForwardInvocation instancesRespondToSelector:anInvocation.selector]) { [anInvocation invokeWithTarget:self.invocation]; }} /* This method must be redone, Message forwarding mechanism USES the information from this method to create NSInvocation object returns nil above method does not perform * / - (NSMethodSignature *) methodSignatureForSelector aSelector: (SEL) { NSMethodSignature*signature = [super methodSignatureForSelector:aSelector];if(! signature){if ([ForwardInvocation instancesRespondToSelector:aSelector]){

            signature = [ForwardInvocation instanceMethodSignatureForSelector:aSelector];

        }

    }

    returnsignature;

}
Copy the code

Console output:

forwardInvocation

ForwardInvocation===appendString:

Finally, Demo: github.com/SionChen/OB… Attach a picture of message forwarding: