preface

Last article OC principle exploration: dynamic method resolution we carried out the exploration of dynamic method resolution, but if there is still no processing, what will be done, today to explore the process behind dynamic method resolution.

The preparatory work

  • Objc4-818.2 – the source code
  • CF source

I. Introduction of message forwarding process

We’ll start with the lookUpImpOrForward function and locate the code in the dynamic method resolution.

  • You can seeDynamic method resolutionI don’t have any more code. I just have togotoThe code andreturnRelated.
  • Now that the process is done, how do we explore itlog_and_fill_cacheFunction, click inside and see.

InstrumentObjcMessageSends function is introduced into

The logMessageSend function is called from the log_and_fill_cache function:

  • We can see thatlogMessageSendSome are printed in the function+,-How and where, and/tmp/msgSends-This is similar to sandbox directory operations.
  • But in thelog_and_fill_cacheAs you can see in the function, onlyobjcMsgLogEnabledfortrueCan be called beforelogMessageSendSo explore whenobjcMsgLogEnabledfortrue.

Global search objcMsgLogEnabled:

  • After searching, this is the only place we can getobjcMsgLogEnabledThe value oftrueSo next we useexternmodifiedinstrumentObjcMessageSendsMake it externally accessible.

InstrumentObjcMessageSends function USES

Add the following code to the project to run the program:

@interface SSLPerson : NSObject
- (void)say1;
@end

@implementation SSLPerson
@end

extern void instrumentObjcMessageSends(BOOL flag);

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        SSLPerson *person = [SSLPerson alloc];
        instrumentObjcMessageSends(YES);
        [person say1];
        instrumentObjcMessageSends(NO);
    }
    return 0;
}
Copy the code

  • The project will still report an error, so let’s go to the sandbox directory to see if there are actually files written.

CMD + shift + g ->

  • We found it in the Sand River cataloguemsgSends-File. Open the file and see what’s in the file.

  • In the document, we see thatresolveInstanceMethod:I’m calling a bunch of functions, and then I’m callingforwardingTargetForSelector:Functions andmethodSignatureForSelector:Delta function, let’s explore the two functions separately.

Ii. Rapid message forwarding process

: we first come to forwardingTargetForSelector function to explore, CMD + shift + 0 open the documentation, and then to search, can get the following result.

  • By reading the documentation, we can see thatforwardingTargetForSelector:Function has aredirectYou can specify an object to do something that is not implementedmethodsLet’s try it out in the project.

Create a new SSLDirector class, and add in the SSLPerson forwardingTargetForSelector method implementation.

@interface SSLPerson : NSObject
- (void)say1;
@end

@implementation SSLPerson
- (id)forwardingTargetForSelector:(SEL)aSelector
{
    return [SSLDirector alloc];
}
@end


@interface SSLDirector : NSObject
- (void)say1;
@end

@implementation SSLDirector
- (void)say1
{
    NSLog(@"SSLDirector say1");
}
@end
Copy the code

Run the program to view the results:

  • SSLDirector say1Print successfully, the program does not report errors, this solution is still very fast, but if this method is not solved, continue to explore.

3. Slow message forwarding process

methodSignatureForSelector

CMD + shift + 0 open the documentation and search methodSignatureForSelector:, can get the following result.

  • By reading the documentation, we know that the method is by returning the method signatureNSMethodSignatureWith as well,forwardInvocation:Method combination, then use code to achieve.

Added in the SSLPerson methodSignatureForSelector and forwardInvocation: method.

@interface SSLPerson : NSObject
- (void)say1;
@end

@implementation SSLPerson
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
    NSLog(@"%s - %@",__func__,NSStringFromSelector(aSelector));
    if (aSelector == @selector(say1)) {
        return [NSMethodSignature signatureWithObjCTypes:"v@:"];
    }
    return [super methodSignatureForSelector:aSelector];
}

- (void)forwardInvocation:(NSInvocation *)anInvocation
{
    
}
@end
Copy the code

Run the program to view the results:

  • The program didn’t crash, and no methods were called.
  • OCThe call method isThe messageSend, also can be said to beThe transaction.The transactionBut if you don’t do it, if you don’t do it, you don’t get an error. So let’s seeforwardInvocationWhat can be done in.

forwardInvocation

CMD + shift + 0 open up documentation, search for forwardInvocation:, and get the following result.

  • Read the document, let’s follow the document to implement in the project.

We add the following code to the forwardInvocation: method to run the program:

- (void)forwardInvocation:(NSInvocation *)anInvocation { SEL aSelector = [anInvocation selector]; SSLDirector *director = [SSLDirector alloc]; if ([director respondsToSelector:aSelector]) { [anInvocation invokeWithTarget:director]; }}Copy the code

  • SSLDirector say1Normal printing, the program did not report an error.

Hopper disassembler CF

Before we return to message forwarding process, do not use instrumentObjcMessageSends function, another way is analyzed.

Run the program first:

  • Because we didn’t do anything, so the program still reports, rightunrecognized selectorThe error.
  • By printing the stack, we see that the call is made___forwarding___,_CF_forwarding_prep_0They all belong toCoreFoundationLet’s look for it in the source code.

CF source code exploration

“Forwarding”, “prep_0” :

  • As shown in the figure above, the result is nothing, which indicates that the source code did not open this part of the content, then use disassembly to continue to explore.

Hopper disassembler CF

With Hopper installed, the executable CoreFoundation dynamic library is ready:

Drag the CoreFoundation dynamic library into Hopper to open it and search for forwarding globally:

  • As seen above___forwarding_prep_0___and____forwarding___Relevant code.

1. The disassemblyforwardingTargetForSelector:

Click ____forwarding___ :

  • Locate the code as shown above to determine if the class respondedforwardingTargetForSelector:If there is a response, go back to execute, if there is no response, jump toloc_64a67.

2. The disassemblyforwardInvocation:

Let’s look at loc_64a67:

  • fromloc_64a67At first, it determines whether the object is_NSZombie_ (Wild pointer)If it isWild pointerJump toloc_64dc1.
  • If it is notWild pointerTo determine whether the class correspondsmethodSignatureForSelector:If there is no response jump toloc_64dd7If so, execute the function and continue down.
  • met_forwardStackInvocation:This is the internal method of the system, no external exposure, continue to execute.
  • theloc_64c19To determine whether the class correspondsforwardInvocation:If there is no response jump toloc_64ec2Execute this function if the response is received.

Through disassembly, we proved that message forwarding has a very clear process at the bottom level and has evidence to rely on. In addition to Hopper, IDA can also explore disassembly.

5. Message forwarding flow chart