preface

Dynamic method resolution is explored in IOS underlying principles of dynamic method resolution. After the dynamic resolution through log auxiliary function to realize forwardingTargetForSelector and methodSignatureForSelector method, that is the message sent by the last process forward

The preparatory work

  • Objc4-818.2 – the source code
  • CF source
  • Disassembly toolHopperandida

forward

Message sending after the dynamic method resolution still did not find the real method implementation, at this time the dynamic method resolution throw IMP = forward_IMP into the message forwarding process. The forwarding process consists of two steps: fast forwarding and slow forwarding

Fast forward

forwardingTargetForSelector

Through the auxiliary function to realize forwardingTargetForSelector log, but specific means nothing, this method to recognize this method below. Open the Xcode, command + shift + 0, then the global search forwardingTargetForSelector

forwardingTargetForSelectorThe meaning is to return an unrecognized messageredirectSpecify an object to receive the message

Examples explore fast forwarding

First define the LWPerson and LWTest classes, and then call the sayHello method in the main function. LWPerson does not implement sayHello method, LWTest class implements sayHello method, LWTest class and LWPerson class may not inherit relationship

@implementation LWPerson

-(id)forwardingTargetForSelector:(SEL)aSelector{
    if (aSelector == @selector(sayHello)) {
        return [[LWTest alloc] init];
    }
    
    return  [super forwardingTargetForSelector:aSelector];
}
@end
Copy the code
@implementation LWTest
-(void)sayHello{
    NSLog(@"---%s---",__func__);
}
@end
Copy the code
int main(int argc, char * argv[]) {

    @autoreleasepool {
        LWPerson * p = [LWPerson alloc];
        [p sayHello];
    }
    return 0;
}
Copy the code
202107 -- 05 17:21:58.201416+0800 objc_msgSend[23131:807070] ----[LWTest sayHello]---
Copy the code

The printed result shows that LWTest class has no relationship with LWPerson class, but it can still be queried at last when it is assigned to LWTest class, and there is no crash message. In fact, the message is searched in the class closely related to LWTest first, but it is not found at last. So the system gives this permission to the developer, and you tell me which object and class can receive this message. The jar is broken, which I didn’t expect.

If you do not give the specified class implementation, fast forwarding is not possible, the system has no bottom line to give you slow forwarding, ridiculous

Slowly forward

Slow forward methodSignatureForSelector is also news to find the last process. Give dynamic method resolution, give fast forwarding, give you a chance to slow forwarding. They say it’s three things, it’s too much for the system to handle, and they’ll give you crash rattail juice

MethodSignatureForSelector means returns a NSMethodSignature object, the object contains by a given selector identifies the description of the method. MethodSignatureForSelector collocation and forwardInvocation use commonly, if methodSignatureForSelector method returns a nil forwardInvocation wouldn’t call

@implementation LWPerson
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
    return  [super methodSignatureForSelector:aSelector];
}

- (void)forwardInvocation:(NSInvocation *)anInvocation{
       NSLog(@"% @ - - -",anInvocation);
}
@end 
Copy the code
202107 -- 05 21:09:59.688392+0800 objc_msgSend[1121:21900] -[LWPerson sayHello]: 
unrecognized selector sent to instance 0x60000001c000
Copy the code

MethodSignatureForSelector at this time of the return value is nil, slow forward completed, melt directly

@implementation LWPerson
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
    if (aSelector == @selector(sayHello)) {
        return [NSMethodSignature signatureWithObjCTypes:"v@:"];
    }
    return  [super methodSignatureForSelector:aSelector];
}

- (void)forwardInvocation:(NSInvocation *)anInvocation{
        NSLog(@"-- - % @ - % @",anInvocation.target,NSStringFromSelector(anInvocation.selector));
}
@end 
Copy the code
202107 -- 05 21:42:47.001759+0800 objc_msgSend[1629:44959] ---<LWPerson: 0x60000000c140>---sayHello
Copy the code

If methodSignatureForSelector return values is NSMethodSignature object, will call forwardInvocation physical processing anInvocation saved NSMethodSignature signature information, There is also the method signature SEL for the target method, and the recipient of the method. The crash message is not reported at this time, of course, can also process anInvocaion transaction

- (void)forwardInvocation:(NSInvocation *)anInvocation{
    LWTest * test = [LWTest alloc];
    anInvocation.target = test;
    anInvocation.selector = @selector(sayBeautiful);
    [anInvocation invoke];
}
Copy the code
202107 -- 05 21:52:29.117134+0800 objc_msgSend[1787:51943] ----[LWTest sayBeautiful]---
Copy the code

The anInvocation target is [LWTest alloc], the anInvocation Selector is @Selector (sayBeautiful), and the [anInvocation invoke] invokes the message. The forwardInvocation is like a distribution center for unrecognized messages and can translate one message to another or simply “eat” some messages. So it doesn’t collapse without processing

Message exchange summary

  • Fast forwarding: PassforwardingTargetForSelectorImplementation, if there is a specified object to receive the message, will go through the specified object search process, if the return isnilTo enter the slow forwarding process
  • Slow forwarding: YesmethodSignatureForSelectorandforwardInvocationCo-realization, ifmethodSignatureForSelectorThe return value isnil, slow search process ends if there is a return valueforwardInvocationThe transaction does not crash without being processed

Message Forwarding Flowchart

The disassembly

Method call crashes and stack information is displayed from__forwarding_prep_0___ It’s called again at the end of the calldoesNotRecognizeSelector. Explore the specific process

__forwarding_prep_0___ is part of the CoreFoundation system library

After downloading the code of CoreFoundation library, there is no global search in the source code, indicating that this content is not provided by Apple

Hopperoridaexplore

Hopper disassembler, we disassembler CoreFoundation executable, to find __forwarding_prep_0___, CoreFoundation executable how to obtain

image listYou can obtain the list of all image filesCoreFoundationFile path to

Global search__forwarding_prep_0___Is found only one, and will be called__forwarding__And into the__forwarding__methods

  • ifforwardingTargetForSelectorMethod not implemented, jumploc_64ad7process
  • ifforwardingTargetForSelectorThe return value of the method isnilJump,loc_64ad7process

At this point, the slow forwarding process is entered

  • ifmethodSignatureForSelectorNo direct jump toloc_64e47process
  • ifmethodSignatureForSelectorReturn value is equal tonilJump toloc_64eacprocess
  • ifmethodSignatureForSelectorObject that returns signature information

  • loc_64e47Flow: Directly report an error and jump toloc_64eacprocess
  • loc_64eacProcess:doesNotRecognizeSelectorCollapse treatment

Returns the forwardInvocation method with signature information to handle the transaction

conclusion

The exploration of the method is basically over. In my opinion, it is the most important to realize the whole thinking and way of the exploration through the exploration method