Message forwarding introduction

The use of instrumentObjcMessageSends method

In the last article we introduced the method resolution approach to dealing with unimplemented method crashes; But clearly this one-size-fits-all approach is not desirable; So is there any other way we can do this?

Log_and_fill_cache (log_and_fill_cache);

The logMessageSend method and the implementer of the method call would be implementer only if objcMsgLogEnabled was set to YES. The implementer method would be implementer only if objcMsgLogEnabled was set to YES.

Log information is stored in/TMP /msgSends-%d. What if objcMsgLogEnabled is set to true? Search for objcMsgLogEnabled in source code:

Found in the method instrumentObjcMessageSends will give objcMsgLogEnabled assignment, so by this method is called, can input method call log information.

Let’s first look at the log output of the unimplemented talk method during execution:

The project must be a MAC project, such as the Command Line Tool project

After running, the final log file is generated:

We open the log file. The log is as follows:

We just printed the calls to object methods. Now let’s look at the calls to class methods:

The generated file is as follows:

We can see that class methods call resolveClassMethod more than object methods. We will focus on a few methods in the red box. The rest are the output of the underlying calls to objc. ResolveInstanceMethod and resolveClassMethod method in the previous article we have described, also know the reason why the call twice, so forwardingTargetForSelector, What is methodSignatureForSelector and doesNotRecognizeSelector? That’s the message forwarding process we’re going to look at next

forward

Fast forward forwardingTargetForSelector

Next, we analyze the process of message forwarding by calling object methods. In the previous file we see forwardingTargetForSelector method calls, so this method is to do what?

Returns the object to which unrecognized messages should first be directed.
Copy the code

6. Most recognized messages are returned to the object to which the message should be directed first. We are now the Teacher class add forwardingTargetForSelector default implementation:

As you can see, although still not solve the problem of collapse, but forwardingTargetForSelector method was successfully invoked, then in forwardingTargetForSelector method to redirect to other objects?

Create a new Student class and implement the talk method in Student. Note that it does not inherit from Person:

@interface Student : NSObject

@end

@implementation Student

- (void)talk {
    NSLog(@"%s", __func__);
}

@end
Copy the code

Then in forwardingTargetForSelector, redirect it to Student class objects:

You can see the result: The talk method in the Student class is called, and Student has no inherited connection to Person or Teacher;

This is a fast forward process;

Slow forward methodSignatureForSelector

So what happens if Student also doesn’t implement the talk method? According to our method performs in the log file to print process can know, if the student did not talk, so will continue to report errors, perform methodSignatureForSelector method, so this method is what mean?

Returns an NSMethodSignature object that contains a description of the method identified by a given selector.

Related Documentation
- forwardInvocation:
Overridden by subclasses to forward messages to other objects.
Copy the code

This method will return an NSMethodSignature object that contains the description of the method being called, namely the signature; And it needs to be used with the fore Invocation:

Can see has been blocked by methodSignatureForSelector to, the following completion forwardInvocation: the implementation of the methods, forwardInvocation: used to interpret the signature, Used with methodSignatureForSelector:

V @ : reading:

  • vsaidvoidType, representingtalkThe return value of the
  • @Represents the caller typeself, i.e.,idtype
  • :saidSEL

The Talk is not implemented all along, if it needs to be handled it can be obtained in the forwardInvocation method, otherwise the method will be lost;

Hopper disassembly

Next, we use the Hopper tool to analyze message forwarding through disassembly;

Run the original code, and when the project crashes, use the BT instruction to see the stack information:

DoesNotRecognizeSelector does not exist in the CoreFoundation framework And ___forwarding___ and _CF_forwarding_prep_0 are triggered, so we try to analyze it in the CoreFoundation source code

CoreFoundation source code download

However, neither forwarding_prep_0 nor forwarding_0 can be found in the source code.

So we can go directly to the CoreFoundation executable for disassembly analysis:

The full path to CoreFoundation is as follows:

/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes /iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation

Next, use Hopper to disassemble the analysis. Open the executable using hopper and select an x86(64 bits) architecture:

Forwarding_prep_0: forwarding_prep_0: forwarding_prep_0: forwarding_prep_0

We analyze its process in the form of pseudo code, and the complete process is as follows:

For assembly analysis, the process is as follows

Flow chart of the message forwarding mechanism