preface

In the last article on dynamic method resolution, we talked about how a slow lookup of messages that were not found would enter dynamic resolution. If the dynamic resolution without processing, by log found something new forwardingTargetForSelector and methodSignatureForSelector, also is the last message lookup process forward.

forward

  • Message forwarding is divided intoFast forwardandSlowly forward, let’s start with quick retweets.

Fast forward

Fast forward forwardingTargetForSelector, what role does this approach, the first to search in objc 818.2 source code, only talk about the way, did not find the specific use. To find the methods document Organizer – > CMD + shift + 0, then search forwardingTargetForSelector

forwardingTargetForSelector



  • If the method doesn’t work, give it to someone else

Code validation

  • The following inOut of the source environmentTo verify the
// .h
@interface WSPerson : NSObject

- (void)sayLike; 

@end

// .m
@implementation WSPerson
- (id)forwardingTargetForSelector:(SEL)aSelector {

    NSLog(- % @ @ "🎈 🎈 🎈 % s", __func__, NSStringFromSelector(aSelector));

    return  [super forwardingTargetForSelector:aSelector];
}

/ / call
WSPerson *person = [[WSPerson alloc] init];
[person sayLike];

@end

Copy the code
  • The execution result is as follows:



  • Will go here, can’t find the method is illustrated in the document, said method can’t find, you can specify a to perform, we add a in WSTeacher sayLike method, and implement, then forwardingTargetForSelector return values, designated as WSTeacher objects:

// WSTeacher.h
@interface WSTeacher : NSObject

- (void)sayLike;

@end

// WSTeacher.m
@implementation WSTeacher
- (void)sayLike {
    NSLog("@" 🎉 🎉 🎉 % s, __func__);
}
@end


// WSPerson.m
@implementation WSPerson

- (id)forwardingTargetForSelector:(SEL)aSelector {

    NSLog(- % @ @ "🎈 🎈 🎈 % s", __func__, NSStringFromSelector(aSelector));

    return [WSTeacher alloc];
}
@end
Copy the code

The running results are as follows:



  • Thus inWSTeacherClass, but there’s a downside to this process,If the specified class also does not implement the methodHow to break? And then we go fromFast forwarding Changes to slow forwarding.

Slowly forward

In auxiliary log msgSends, we can see that when forwardingTargetForSelector followed a methodSignatureForSelector method, namely fast forwarding didn’t find, Will enter methodSignatureForSelector method

methodSignatureForSelector

  • Let’s look atmethodSignatureForSelectorDescription:



  • The document says that this method returns a method signatureNSMethodSignatureAnd must be created during the forwarding of the messageNSInvocation, which is the realizationforwardInvocationmethods

Code validation

  • To verify that the code can come in, first remove itWSTeacherIn thesayLikeImplementation:
@implementation WSPerson 

// Fast forward
- (id)forwardingTargetForSelector:(SEL)aSelector {
    NSLog(- % @ @ "🎈 🎈 🎈 % s", __func__, NSStringFromSelector(aSelector));
    return  [WSTeacher alloc];
}

// Slow forwarding
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    NSLog(- % @ @ "🍊 🍊 🍊 % s", __func__, NSStringFromSelector(aSelector));
    return [super methodSignatureForSelector:aSelector];
}
@end
Copy the code
  • Let’s run it again

Question: methodSignatureForSelector incredibly didn’t go, is this why?

  • Let’s take a look at this code and find outforwardingTargetForSelectorSpecifies theWSTeacherTo perform a search, butWSTeacherIt’s not implemented in the, so it’s not going to go, you can get rid of itFast forwardTry again:

The code comes in as expected, and it says in the document, it needs to go backThe method signatureAnd the implementationforwardInvocationMethod, we modify this code:

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

- (void)forwardInvocation:(NSInvocation *)anInvocation {
    NSLog(@ % @ - % @ "", anInvocation.target, NSStringFromSelector(anInvocation.selector));
}
Copy the code

The running results are as follows:

So youYou avoid a crash.forwardInvocationSaves the method signature information inNSInvocationYou can also handle related transactions, such asSpecify the implementation:

- (void)forwardInvocation:(NSInvocation *)anInvocation {
    NSLog(@ % @ - % @ "", anInvocation.target, NSStringFromSelector(anInvocation.selector));
    WSTeacher *t = [WSTeacher alloc];
    if ([self respondsToSelector:anInvocation.selector]) {
        [anInvocation invoke];
    } else if ([t respondsToSelector:anInvocation.selector]) {
        [anInvocation invokeWithTarget:t];
    } else {
        anInvocation.target = t;
        anInvocation.selector = @selector(sayLearn);
        [anInvocation invoke];
        NSLog(@" The method cannot be found"); }}Copy the code
  • forwardInvocationcanSpecify to a class to call, it can also beChange your target, change your selectorEtc.
  • forwardInvocationThe process is essentially telling the programsayLikeIf this method is still in place, the program will not do exception handling, but it does exist and can do it autonomously. However, this series of processes down, will cause a waste of resources.

conclusion

  • Quick forward: callforwardingTargetForSelectorTo implement the fast forward process, you can return the object you want to implement for yourself to implement the method, ifReturns nilWill enter theSlowly forwardProcess.
  • Slow forwarding: Slow forwarding needs to be invokedmethodSignatureForSelectorandforwardInvocationAchieve together,methodSignatureForSelectorYou need to return the method signature, andforwardInvocationIt can do some processing, such as specifying object processing, specifying methods and so on. If there is no method signature, the slow search ends.

Message forwarding diagram

Look at the stack

  • If these forwarding processes are not processed, the operation must be an error, so how to view, we can useThe bt commandPrint stack:



The analysis shows that indoesNotRecognizeSelectorAnd then there are two ways to do it___forwarding___and__forwarding_prep_0___Methods, both of these methods are thereCoreFoundationLibrary, in accordance with past experience to source analysis under the role, but in the sourceI didn't find it,CoreFoundationThe library is not fully open source, so what? We can look at the dynamic library.

  • Through the firstimage listCommand to find the dynamic library path, and then findCoreFoundationThe dynamic library

  • Then use theHopperorIDATo analyze, this article mainly inHopperIn the analysis

The disassembly (Hopper)

Analysis of the

Drag the found CoreFoundation dynamic library into the Hopper

  • And then click on the leftlabelsearch__forwarding_prep_0___, mainly checkPseudo code



  • We see__forwarding_prep_0___In the call___forwarding___After getrax, double-click to enter___forwarding___Methods:



  • Here is the judgmentforwardingTargetForSelectorThere is no implementation, if not implementation will goloc_64ad7, or slow forwarding:

  • In slow forwarding, check whether the object is a zombie_NSZombie_, and then judgemethodSignatureForSelectorWhether to achieve, did not achieve to goloc_64e47
  • ifmethodSignatureForSelectorThe return value isnil, then goloc_64eacError
  • ifmethodSignatureForSelectorIf there is a return value, it will determineforwardStackInvocationMethod, and then do the rest of the processing

conclusion

  • hopperYou can also look at the process, but the process is not so comfortable and you have to analyze it slowly, you have to analyze it slowly, and you can still analyze and process the forwarding process.