1. Review

The dynamic method parsing phase was analyzed in the previous post, Runtime 4: Dynamic Method Parsing. This post focuses on the third phase of message sending — > message forwarding.

Dynamic method parsing, if not processed, will enter the message sending process, so what methods will be called by the message forwarding process?

Between now and then, in apple’s source found a magic methods instrumentObjcMessageSends inside, this is a way to print the log.

void instrumentObjcMessageSends(BOOL flag)
{
    bool enable = flag;

    // Shortcut NOP
    if (objcMsgLogEnabled == enable)
        return;

    // If enabling, flush all method caches so we get some traces
    if (enable)
        _objc_flush_caches(Nil);

    // Sync our log file
    if(objcMsgLogFD ! =- 1)
        fsync (objcMsgLogFD);

    objcMsgLogEnabled = enable;
}
Copy the code

So let’s write 🌰 to see what methods are called for message forwarding.

extern void instrumentObjcMessageSends(BOOL flag);

int main(int argc, const char * argv[]) {
    @autoreleasepool {

        instrumentObjcMessageSends(YES);
        LGPerson *person = [LGPerson alloc];
        [person sayInstanceMethod];
        instrumentObjcMessageSends(NO);
        NSLog(@"Hello, World!");
    }
    return 0;
}
Copy the code

Go to Mac Access -> Go -> enter/TMP /msgSends to open the file directory

Double-click to open the generated msgSends-5425 log file

From the log file can be found that the dynamic method invocation resolveInstanceMethod: method is called after forwardingTargetForSelector, methodSignatureForSelector: , resolveInstanceMethod: and doesNotRecognizeSelector: method. So let’s explore each of them.

2. Fast forwarding

From Xcode documentation can search to forwardingTargetForSelector method of definition, and usage.

  • ForwardingTargetForSelector definition

Returns an object to which an unrecognized message is directed. To put it bluntly, look for a recipient, can also be understood as “take the blame”, haha!

- (id)forwardingTargetForSelector:(SEL)aSelector{
	NSLog(@"%s - %@",__func__,NSStringFromSelector(aSelector));
	return [JPStudent alloc];
}
Copy the code
  • ForwardingTargetForSelector code implementation

Calling [person sayHello] is a sure way to break when the JPPerson class doesn’t implement the sayHello method, but let’s leave it to the sayHello guy.

If the runtime does not find the implementation of the invoked method in the first step of message forwarding, the current receiver has a second chance to process. At this moment the run-time system calls – (id) forwardingTargetForSelector (SEL) aSelector method, this method can return a can deal with the object, the runtime system will find, according to the object returned if find the jump to the corresponding method of implementation, message forwarding.

3. Slow forwarding

If there is no blame for fast forwarding, the programmer will not deal with it and enter the slow forwarding process.

3.1 Method Signature

  • MethodSignatureForSelector definition

Returns an NSMethodSignature object that contains the description of the method identified by the given selector. Is simply the method signature, methodSignatureForSelector must be used together with forwardInvocation

	//// Slow forwarding
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
	NSLog(@"%s - %@",__func__,NSStringFromSelector(aSelector));
	if (aSelector == @selector(sayHello)) {
		return [NSMethodSignature signatureWithObjCTypes:"v@:@"];
	}
	return [super methodSignatureForSelector:aSelector];
}
Copy the code

3.2 Message Forwarding

  • ForwardInvocation definition

The above method signature information is stored in the NSInvocation, and the method number is obtained from SEL SEL = [anInvocation selector]

- (void)forwardInvocation:(NSInvocation *)anInvocation{
		// NSLog(@"%@ - %@",anInvocation.target,NSStringFromSelector(anInvocation.selector));
	JPStudent *s = [JPStudent alloc];
	if ([self respondsToSelector:anInvocation.selector]) {
		[anInvocation invoke];
	}else if ([s respondsToSelector:anInvocation.selector]){
		[anInvocation invokeWithTarget:s];
	}else{
		NSLog(@"%s - %@",__func__,NSStringFromSelector(anInvocation.selector)); }}Copy the code

4. The message is not processed

If the above methods are not implemented, can perform – (void) doesNotRecognizeSelector aSelector: (SEL) this method

Apple: I designed so clever, give you programmer so many times remedial opportunity, you unexpectedly do not deal with, ah! Well, I can’t crash the program!

// If none of the above processes is processed and no message handlers are found, this is executed- (void)doesNotRecognizeSelector:(SEL)aSelector {
	NSLog(@" Can't find a way");
}
Copy the code

5. To summarize

  • The dynamic method parsing is not processed, and the message forwarding process is entered

  • The message forwarding process includes fast forwarding and slow forwarding

  • DoesNotRecognizeSelector can be implemented to prevent crashes if the message forwarding phase, fast forwarding and slow forwarding are not handled

  • Message forwarding Flowchart

More to come

🌹 just like it 👍🌹

🌹 feel learned, can come a wave, collect + concern, comment + forward, lest you can’t find me next 😁🌹

🌹 welcome everyone to leave a message to exchange, criticize and correct, learn from each other 😁, improve self 🌹