What to learn from this article:

  • Explore the causes of message forwarding
  • Why does the console prompt unrecognized Selector sent to instance when printing?
  • The console prints a lookup flowchart
  • Dynamic method resolution
  • Bit operation to achieve singleton interpretation
  • resolveMethod_locked
  • Dynamic resolution of instance methods
  • Dynamic resolution of class methods
  • Integrate dynamic method resolution for instance/class objects
  • A brief description of OOP and AOP

Explore the causes of message forwarding

  • I am inFFPersonClass creates an instance methodlikeGirlsBut it didn’t happen
  • Then I ammain.mFile initializationFFPersonClass and calllikeGirlsmethods
  • Classic error:'-[FFPerson likeGirls]: unrecognized selector sent to instance 0x1006106e0'Cannot find an instance of the method, i.eUnrealized..

Case code

@interface FFPerson : NSObject
// Declare an instance method
- (void)likeGirls;
@end

@implementation FFPerson
// The instance method is not implemented
@end


int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        
        // Initialize the instance object of FFPerson
        FFPerson *person = [FFPerson alloc];
        // Call the likeGirls instance method
        [person likeGirls];
    }
    return 0;
}
Copy the code

Console classic error:

2021-07-04 09:58:10.002598+0800 001- Explore the cause of message forwarding [12184:1009168] -[FFPerson likeGirls]: Unrecognized Selector sent to instance 0x1006106e0 2021-07-04 09:58:10.017465+0800 001- Explore the cause of message forwarding [12184:1009168] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[FFPerson likeGirls]: unrecognized selector sent to instance 0x1006106e0' *** First throw call stack: ( 0 CoreFoundation 0x00007fff205a56af __exceptionPreprocess + 242 1 libobjc.A.dylib 0x00007fff202dd3c9 objc_exception_throw + 48 2 CoreFoundation 0x00007fff20627c85 -[NSObject(NSObject) __retain_OA] + 0 3 CoreFoundation 0x00007fff2050d07d ___forwarding___ + 1467 4 CoreFoundation 0x00007fff2050ca38 _CF_forwarding_prep_0 + 120 6 libdyld.dylib 0x00007fff2044e621 start + 1 ) libc++abi.dylib: terminating with uncaught exception of type NSException *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[FFPerson likeGirls]: unrecognized selector sent to instance 0x1006106e0' terminating with uncaught exception of type NSException (lldb)Copy the code

Screenshot supplement:Question point 1: when the method is not implemented when why the reportunrecognized selector sent to instanceWhat about this mistake?

Through the previous several articles to share, know a message search process:

  1. Quick Find process (exploration completed)->
  2. Slow find process (exploration completed)->
  3. Dynamic Method forward process (explored in this article)

When the message enters the slow lookup process it ends up atLookUpImpOrForwardFor this core method, I’ll continue the slow lookup process for this articleCan't matchThe case of LookUpImpOrForword follows, soStarting pointIn this method, global search:

Objc4 source solution (intercept key source) :

IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior)
{
    const IMP forward_imp = (IMP)_objc_msgForward_impcache;
    IMP imp = nil;

    for (unsigned attempts = unreasonableClassCount();;) {

        if (slowpath((curClass = curClass->getSuperclass()) == nil)) {
            // When the message can not be found after the fast and slow search process has gone through
            // IMP will give a default value forward_IMP and enter the message forwarding process
            imp = forward_imp;
            break; }}return imp;
}
Copy the code

The LookUpImpOrForward method shows that an IMP is assigned a forward_IMP by default if it is not found. It stands for (IMP)_objc_msgForward_impcache, and then search globally for (IMP)_objc_msgForward_impcache:

Continuing with the global lookup for __objc_msgForward, I’m still concerned with the arm64 architecture:

A global search for __objc_forward_handler failed to find what I was looking for

Remove one of the underscore “_” from __objc_forward_handler and search for _objc_forward_handler again

Now, when I print the console, why does it prompt you to send an unrecognized selector to instance?

Exploration flow chart:

Conclusion:

  1. From the console print information unrecognized selector sent to instance as the starting point, it is learned that the response process of deep digging method

  2. The analysis of LookUpImpOrForward shows that the default value forward_IMP, objc_msgForward_impcache, is assigned to IMP if a slow search fails to find it

  3. By looking up objc_msgForward_impcache, it is found that it is only an intermediate function in assembly, and it really points to objc_msgForward

  4. The analysis of objc_msgForward shows that the method calls objc_forward_handle to get the return value stored in register X17, and then the TailCallFunctionPointer method is used to go to the imp address in register X17

  5. The _objc_forward_handler will give the default implementation of objc_defaultForwardHandle. When the IMP has not found the IMP, it will enter objc_defaultForwardHandle. Print the error message.

Dynamic method resolution

When all the above methods have been completed, the IMP corresponding to sel cannot be found, this is the end of the loop search, the next step into the dynamic method resolution

Objc4 source solution (intercept key source) :

IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior)
{
    const IMP forward_imp = (IMP)_objc_msgForward_impcache;
    IMP imp = nil;

    for (unsigned attempts = unreasonableClassCount();;) {

        if (slowpath((curClass = curClass->getSuperclass()) == nil)) {
            // When the message can not be found after the fast and slow search process has gone through
            // IMP will give a default value forward_IMP and enter the message forwarding process
            imp = forward_imp;
            break;
        }
        // Find imp for sel
        if (fastpath(imp)) {
            gotodone; }}/** * If the done branch is found, the done branch will be passed to the done branch. * If the done branch is not found, the done branch will be passed to the resolveMethod_locked function. Ensure that a lookUpImpOrForward * executes resolveMethod_locked */ only once 
    if (slowpath(behavior & LOOKUP_RESOLVER)) {
        behavior ^= LOOKUP_RESOLVER;
        return resolveMethod_locked(inst, sel, cls, behavior);
    }
    return imp;
}
Copy the code

Bit operation to achieve singleton interpretation

Interpretation of operation symbols:

  • & : Bitwise and, is the binocular operator. Its function is the corresponding binary phase of the two numbers involved in the operation. The result bit is 1 only if both corresponding binaries are 1.

  • ^ : xor by bit, comparing binary bits, the same position of different digits is 1, the same is 0

  • ^= : bitwise xOR, compares bits in the same position, different numbers are 1, same numbers are 0, and assigns the result to the left of the operator.

Prerequisites (Initial value)

behavior = 3

LOOKUP_RESOLVER = 2

Initial judgment operation

Behavior & LOOKUP_RESOLVER = 3&2 = 0x11&0x10 = 0x10 = 2

Reset behaivor

Behavior = Behavior ^ LOOKUP_RESOLVER = 3 ^ 2 = 0x11 ^ 0x10 = 0x01 = 1

Enter the judgment operation again (second to infinite times)

Behavior & LOOKUP_RESOLVER = 1&2 = 0x01&0x10 = 0x00 = 0

conclusion

This ensures that each lookUpImpOrForward function executes resolveMethod_locked (dynamic method resolution) at most once until the behavior is reassigned

resolveMethod_locked

Significance of this method: When you call a method, you first go into the quick lookup process of the message -> and then into the slow lookup process of the message. When the underlying source code has given you the method twice and still can’t find where you implemented it, imp=nil, theoretically the program should crash, but from a developer’s point of view, This makes the framework unstable, or the system unfriendly. So the framework has decided to give you one more chance to save the world by providing you with a custom IMP return, resolveMethod_locked this function is the entry point for dynamic message forwarding.

static NEVER_INLINE IMP
resolveMethod_locked(id inst, SEL sel, Class cls, int behavior)
{
    runtimeLock.assertLocked(a);ASSERT(cls->isRealized());

    runtimeLock.unlock(a);if (! cls->isMetaClass()) {
        // Imp is the instance method
        resolveInstanceMethod(inst, sel, cls);
    } 
    else {
        // Imp is a class method
        resolveClassMethod(inst, sel, cls);
        if (!lookUpImpOrNilTryCache(inst, sel, cls)) {
            resolveInstanceMethod(inst, sel, cls); }}ResolveInstanceMethod (); // The imp has been dynamically added to sel
    // So try again
    return lookUpImpOrForwardTryCache(inst, sel, cls, behavior);
}
Copy the code

There are three key functions in this function:

  • ResolveInstanceMethod: The instance method dynamically adds the IMP

  • ResolveClassMethod: The class method dynamically adds the IMP

  • LookUpImpOrForwardTryCache, after finish add, to go back to the previous slow search process again

resolveInstanceMethod

/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * call + resolveInstanceMethod parse instance method, find out the ways to class to be added to the CLS. * CLS may be metaclass or non-metaclass. * Does not check if the method already exists. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
static void resolveInstanceMethod(id inst, SEL sel, Class cls)
{
    runtimeLock.assertUnlocked(a);ASSERT(cls->isRealized());
    SEL resolve_sel = @selector(resolveInstanceMethod:);

    // When you implement resolveInstanceMethod, return is not entered here
    The resolveInstanceMethod function returns NO by default
    if (!lookUpImpOrNilTryCache(cls, resolve_sel, cls->ISA(/*authenticated*/true))) {
        return;
    }

    // The system sends you a message at this point: resolve_sel
    // When your class detects the message and does the processing
    BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
    bool resolved = msg(cls, resolve_sel, sel);

    // At this point, the system will look again, and this function will eventually trigger LookUpImpOrForward
    IMP imp = lookUpImpOrNilTryCache(inst, sel, cls);

    if (resolved  &&  PrintResolving) {
        if (imp) {
            _objc_inform("RESOLVE: method %c[%s %s] "
                         "dynamically resolved to %p", 
                         cls->isMetaClass()?'+' : The '-', 
                         cls->nameForLogging(), sel_getName(sel), imp);
        }
        else {
            // Method resolver didn't add anything?
            _objc_inform("RESOLVE: +[%s resolveInstanceMethod:%s] returned YES"
                         ", but no new implementation of %c[%s %s] was found",
                         cls->nameForLogging(), sel_getName(sel), 
                         cls->isMetaClass()?'+' : The '-', 
                         cls->nameForLogging(), sel_getName(sel)); }}}Copy the code

resolveClassMethod

/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * analytical methods * call + resolveClass method, find out the ways to class to be added to the CLS. * CLS should be a metaclass. * Does not check if the method already exists. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
static void resolveClassMethod(id inst, SEL sel, Class cls)
{
    runtimeLock.assertUnlocked(a);ASSERT(cls->isRealized());
    ASSERT(cls->isMetaClass());

    // When you implement resolveClassMethod, return is not entered here
    // The resolveClassMethod function returns NO by default
    if (!lookUpImpOrNilTryCache(inst, @selector(resolveClassMethod:), cls)) {
        // Resolver not implemented.
        return;
    }
    
    // Nonmeta Fault tolerant processing
    Class nonmeta;
    {
        mutex_locker_t lock(runtimeLock);
        nonmeta = getMaybeUnrealizedNonMetaClass(cls, inst);
        if(! nonmeta->isRealized()) {
            _objc_fatal("nonmeta class %s (%p) unexpectedly not realized",
                        nonmeta->nameForLogging(), nonmeta); }}// The system sends you a resolveClassMethod message at this point
    // When your class detects the message and does the processing
    BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
    bool resolved = msg(nonmeta, @selector(resolveClassMethod:), sel);

    // At this point, the system will look again, and this function will eventually trigger LookUpImpOrForward
    IMP imp = lookUpImpOrNilTryCache(inst, sel, cls);

    if (resolved  &&  PrintResolving) {
        if (imp) {
            _objc_inform("RESOLVE: method %c[%s %s] "
                         "dynamically resolved to %p", 
                         cls->isMetaClass()?'+' : The '-', 
                         cls->nameForLogging(), sel_getName(sel), imp);
        }
        else {
            // Method resolver didn't add anything?
            _objc_inform("RESOLVE: +[%s resolveClassMethod:%s] returned YES"
                         ", but no new implementation of %c[%s %s] was found",
                         cls->nameForLogging(), sel_getName(sel), 
                         cls->isMetaClass()?'+' : The '-', 
                         cls->nameForLogging(), sel_getName(sel)); }}}Copy the code

resolveClassMethod & resolveInstanceMethod

// Return NO by default. If the user does not implement this method, the program does not return
+ (BOOL)resolveClassMethod:(SEL)sel {
    return NO;
}
// Return NO by default. If the user does not implement this method, the program does not return
+ (BOOL)resolveInstanceMethod:(SEL)sel {
    return NO;
}
Copy the code

lookUpImpOrForwardTryCache

Excessive function

IMP lookUpImpOrForwardTryCache(id inst, SEL sel, Class cls, int behavior)
{
    return _lookUpImpTryCache(inst, sel, cls, behavior);
}
Copy the code

_lookUpImpTryCache

After the dynamic add method is passed, try again to find the newly added IMP corresponding to sel

ALWAYS_INLINE
static IMP _lookUpImpTryCache(id inst, SEL sel, Class cls, int behavior)
{
    runtimeLock.assertUnlocked(a);// When the class is uninitialized, lookUpImpOrForward is entered
    // The handler does not cache any methods in it
    if (slowpath(! cls->isInitialized())) {
        return lookUpImpOrForward(inst, sel, cls, behavior);
    }
    
    // Find by assembly
    IMP imp = cache_getImp(cls, sel);
    if(imp ! =NULL) goto done;
    // Share cache lookup
#if CONFIG_USE_PREOPT_CACHES
    if (fastpath(cls->cache.isConstantOptimizedCache(/* strict */true))) {
        imp = cache_getImp(cls->cache.preoptFallbackClass(), sel);
    }
#endif
    // If not, prove that the method really does not exist, i.e. rely on the dynamic addition of the method
    // Then enter the slow lookup process of the method again
    if (slowpath(imp == NULL)) {
        return lookUpImpOrForward(inst, sel, cls, behavior);
    }

done:
    // If the imp already exists, and the IMP is assigned forward_IMP by default
    // Return imp as nil;
    if ((behavior & LOOKUP_NIL) && imp == (IMP)_objc_msgForward_impcache) {
        return nil;
    }
    return imp;
}
Copy the code

Both resolveMethod_locked and resolveInstanceMethod execute lookUpImpOrNilTryCache. Why do they execute lookUpImpOrNilTryCache twice?

Dynamic resolution of instance methods

Phase 1: Restore from source

Through the source code has been known to give developers a chance to make up, through resolveInstanceMethod this method, here in the program simulation of this method;

@implementation FFPerson
+ (BOOL)resolveInstanceMethod:(SEL)sel {
    NSLog(@"Manually intervene in resolveInstanceMethod :%@-%@",self,NSStringFromSelector(sel));
    return [super resolveInstanceMethod:sel];
}
@end
Copy the code

Console print result:

021-07-04 16:29:18.779380+0800 001- Explore the cause of message forwarding [14290:1164992] Manually interfere with resolveInstanceMethod: ffperson-likegirls 2021-07-04 16:29:18.779920+0800 001- Explore the cause of message forwarding [14290:1164992] Manually interfere with resolveInstanceMethod: ffperson-likegirls 2021-07-04 16:29:18.780020+0800 001- Explore the cause of message forwarding [14290:1164992] -[FFPerson likeGirls]: Unrecognized Selector sent to instance 0x100606150 2021-07-04 16:29:18.780574+0800 001- Explore the cause of message forwarding [14290:1164992] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[FFPerson likeGirls]: unrecognized selector sent to instance 0x100606150'Copy the code

This is to be expected because the method is not yet implemented, but it prints out my manual intervention before the crash, which means there is a remedy before the crash.

Phase 2: Implement \ Dynamically add IMP

Case code

@implementation FFPerson

// Create an IMP
- (void)prettyGirls {
    NSLog(@"%s",__func__);
}

+ (BOOL)resolveInstanceMethod:(SEL)sel {
    if (sel == @selector(likeGirls)) {
        // When sel==likeGirls, dynamically assign a new IMP to the sel (prettyGirls)
        IMP prettyGirlsImp = class_getMethodImplementation(self, @selector(prettyGirls));
        Method method = class_getInstanceMethod(self, @selector(prettyGirls));
        const char * type = method_getTypeEncoding(method);
        return  class_addMethod(self, sel, prettyGirlsImp, type);
    }
    NSLog(@"Manually intervene in resolveInstanceMethod :%@-%@",self,NSStringFromSelector(sel));
    return [super resolveInstanceMethod:sel];
}
@end
Copy the code

Console print result:

2021-07-04 16:46:17.907703+0800 001- Exploring the Cause of message forwarding [14403:1174269] -[FFPerson prettyGirls]Copy the code

Dynamic resolution of class methods

Case code

@interface FFPerson : NSObject
// Declare an instance method
+ (void)enjoyLife;
@end

@implementation FFPerson
// This class method is not implemented
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        
        // Initialize the instance object of FFPerson
        FFPerson *person = [FFPerson alloc];
        // Call an unimplemented class method
        [FFPerson enjoyLife];
    }
    return 0;
}
Copy the code

Console classic error

2021-07-04 17:15:18.903974+0800 001- Explore the cause of message forwarding [14552:1188609] +[FFPerson enjoyLife]: Unrecognized Selector sent to class 0x100008188 2021-07-04 17:15:18.904697+0800 001- Explore the cause of message forwarding [14552:1188609] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+[FFPerson enjoyLife]: unrecognized selector sent to class 0x100008188' *** First throw call stack: ( 0 CoreFoundation 0x00007fff205a56af __exceptionPreprocess + 242 1 libobjc.A.dylib 0x00007fff202dd3c9 objc_exception_throw + 48 2 CoreFoundation 0x00007fff20627bdd __CFExceptionProem + 0 3 CoreFoundation 0x00007fff2050d07d  ___forwarding___ + 1467 4 CoreFoundation 0x00007fff2050ca38 _CF_forwarding_prep_0 + 120 6 libdyld.dylib 0x00007fff2044e621 start + 1 ) libc++abi.dylib: terminating with uncaught exception of type NSException *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+[FFPerson enjoyLife]: unrecognized selector sent to class 0x100008188' terminating with uncaught exception of type NSException (lldb)Copy the code

Phase 1: Restore from source

Through the source code has been known to give developers a chance to make up, through resolveInstanceMethod this method, here in the program simulation of this method;

@implementation FFPerson
+ (BOOL)resolveInstanceMethod:(SEL)sel {
    NSLog(@"Manually intervene in resolveInstanceMethod :%@-%@",self,NSStringFromSelector(sel));
    return [super resolveInstanceMethod:sel];
}
@end
Copy the code

Console printing

2021-07-04 17:19:48.778680+0800 001- Explore the cause of message forwarding [14583:1191738] Manually intervene in resolveInstanceMethod :FFPerson-_dynamicContextEvaluation:patternString: 2021-07-04 17:19:48.791081+0800 001- Explore the cause of message forwarding [14583:1191738] Manually involve resolveInstanceMethod: ffperson-enjoyLife 2021-07-04 17:19:48.791255+0800 001- Explore the cause of message forwarding [14583:1191738] +[FFPerson enjoyLife]: Unrecognized Selector sent to class 0x1000081a8Copy the code

This is to be expected because the method is not yet implemented, but it prints out my manual intervention before the crash, which means there is a remedy before the crash.

Phase 2: Implement \ Dynamically add IMP

Case code

@implementation FFPerson

// Create an IMP for metaclass object creation
+ (void)enjoyTime {
    NSLog(@"%@ - %s",self,__func__);
}

+ (BOOL)resolveClassMethod:(SEL)sel {
    
    if (sel == @selector(enjoyLife)) {
        
        IMP enjoyTimeImp = class_getMethodImplementation(objc_getMetaClass("FFPerson"), @selector(enjoyTime));
        Method method = class_getInstanceMethod(objc_getMetaClass("FFPerson"), @selector(enjoyTime));
        const char * type = method_getTypeEncoding(method);
        return  class_addMethod(objc_getMetaClass("FFPerson"), sel, enjoyTimeImp, type);
    }
    NSLog(@"Manually intervene in resolveInstanceMethod :%@-%@",self,NSStringFromSelector(sel));
    
    return [super resolveClassMethod:sel];
}
Copy the code

Console print result:

2021-07-04 17:40:30.032916+0800 001- Discover the cause of message forwarding [14750:1203053] FFPerson - +[FFPerson enjoyTime]Copy the code

Conclusion:

  1. Manually add resolveInstanceMethod/resolveClassMethod, print to verify any developers operating space before the crash

  2. Dynamically add a new IMP based on the current sel lookup

  3. It has been demonstrated that this method can be used. At this point, the imp corresponding to sel (likeGirls/enjoyLife) is no longer likeGirls/enjoyLife, but the prettyGirls/enjoyTime that I dynamically specify

Question 3: the class dynamic method resolution will enter the resolveClassMethod, and then according to the judgment may enter the resolveInstanceMethod again, why?

Integrate dynamic method resolution for instance/class objects

Back to reality, daily development can’t be implemented in each class a resolveInstanceMethod/resolveClassMethod, this is the real scene, then here by creating NSObject classification way of implementation in this category:

NSObject+FF

@implementation NSObject (FF)

// Create an IMP for the instance object
- (void)prettyGirls {
    NSLog(@"%s",__func__);
}

// Create an IMP for metaclass object creation
+ (void)enjoyTime {
    NSLog(@"%@ - %s",self,__func__);
}

#pragma clang diagnostic push
// Let the compiler ignore errors
#pragma clang diagnostic ignored "-Wundeclared-selector"

+ (BOOL)resolveInstanceMethod:(SEL)sel {
    
    NSLog(@"Manually intervene in resolveInstanceMethod :%@-%@",self,NSStringFromSelector(sel));
    
    if (sel == @selector(likeGirls)) {
        
        IMP prettyGirlsImp = class_getMethodImplementation(self, @selector(prettyGirls));
        Method method = class_getInstanceMethod(self, @selector(prettyGirls));
        const char * type = method_getTypeEncoding(method);
        return  class_addMethod(self, sel, prettyGirlsImp, type);
    } else if (sel == @selector(enjoyLife)) {
        
        IMP enjoyTimeImp = class_getMethodImplementation(objc_getMetaClass("FFPerson"), @selector(enjoyTime));
        Method method = class_getInstanceMethod(objc_getMetaClass("FFPerson"), @selector(enjoyTime));
        const char * type = method_getTypeEncoding(method);
        return  class_addMethod(objc_getMetaClass("FFPerson"), sel, enjoyTimeImp, type);
    }

    return NO;
}
#pragma clang diagnostic pop
@end
Copy the code

main.m

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        
        FFPerson *person = [FFPerson alloc];
        // Call an unimplemented instance method
        [person likeGirls];
        // Call an unimplemented class method
        [FFPerson enjoyLife];
        
    }
    return 0;
}
Copy the code

Print results (some key prints)

2021-07-04 18:06:55.491847+0800 001- Explore the cause of message forwarding [14926:1216030] Manually involve resolveInstanceMethod: ffperson-likegirls 2021-07-04 18:06:55.491952+0800 001- Explore the cause of message forwarding [14926:1216030] -[NSObject(FF) prettyGirls] 2021-07-04 18:06:55.492192+0800 001- Explore the cause of message forwarding [14926:1216030] manually intervene at resolveInstanceMethod: ffperson-enjoyLife 2021-07-04 18:06:55.502342+0800 001- Explore the cause of message forwarding [14926:1216030] FFPerson - +[NSObject(FF) enjoyTime]Copy the code

Complete the Demo

Question4: What is the role of dynamic method resolution? Why does Apple design this way?

Oop and aop

  • Oop: Object oriented programming, who does what, the division of labor is very clear.

    • Benefits:couplingHad a low degree of
    • Pain point: A lotredundantCode, the usual solution isextract, then there will be apublicClass, everyone on the public classintegration, then everyone on the public classStrong dependence on“, also represents the appearanceStrong coupling
  • Aop: Aspect oriented programming, an extension of OOP

    • Cut point: To cut intomethodsAnd cutclass, such as in the example aboveenjoyLifeandFFPerson
    • Advantages:No intrusion on servicesThrough theDynamic wayWill some methods be carried outinjection
    • Cons: Made some judgment, implemented a lotHas nothing to do with the code, includingSystems approach, resulting inPerformance overhead. willinterruptThe method of appleDynamic forwardProcess.

Answer questions raised in the process of source code exploration:

Why are methods reported when they are not implementedunrecognized selector sent to instanceWhat about this mistake?

If the method is not implemented, and you go into a slow lookup of the method, you still can’t find it, you will give the method a default value of _objc_msgForward_impcache, which will eventually find objc_defaultForwardHandle through a series of look-up, This function is marked with an explicit error message.

Both the resolveMethod_locked and resolveInstanceMethod functions execute lookUpImpOrNilTryCache. Why do they execute it twice?

Through debugging, it is found that the reason is that more methods such as respondsToSelector are executed. The specific reason has not been explored and will be added later

A class dynamic method resolution goes to resolveClassMethod, and then, depending on what you decide, it goes back to resolveInstanceMethod. Why?

Normal dynamic method resolution of class objects will enter the resolveClassMethod, which is beyond doubt. However, the search process of class methods is in the metaclass, so it can be seen from the pointing graph of ISA that the search process of class methods also has inheritance relationship, and will always look up to find superMetaClass. You find rootMetaClass, you find NSObject, and at this level, all the methods are instance methods to NSObject so resolveInstanceMethod is called.

What is the role of dynamic method resolution? Why does Apple design this way?

First of all, this is Apple’s last chance for developers to do something you can’t find. Writing the resolveInstanceMethod method in the class of NSNbject means that all global methods are missing, so we can listen to them. Create an NSObject category “project name _ module _ thing”, for example, FF_Home_didClickDetail, the didClickDetail event is not implemented, then report to the background immediately which project, which module, which time caused the crash, To help you save your KPIs, the second thing is to prevent carsh from jumping to Home when the time is not implemented.

Comprehension:

Through this process of exploration, to become a good developer, you must give others more fault tolerance.