preface

Methods occupy an important position in programming, we are familiar with methods and strange. What is familiar is that it is used every day, and what is strange is that everyone has only a half-understanding of the underlying implementation. The quick search process and slow search process of the method were explored before, and the search process at the bottom of the method had a certain understanding. If neither the fast lookup process nor the slow lookup process has a method lookup implementation, what will the following process look like, and Apple will give a chance to dynamic method resolution

The preparatory work

  • Objc4-818.2 – the source code
  • Iced 🍉

Case analysis

Create a LWPerson class and declare a sayHello method that is not implemented

int main(int argc, char * argv[]) {
 
    @autoreleasepool {
        LWPerson * perosn  = [LWPerson alloc];
        [perosn sayHello];
 
    }
    return 0;
}
Copy the code

DoesNotRecognizeSelector or unrecognized selector sent to instance doesNotRecognizeSelector doesNotRecognizeSelector Search in the source code to find the implementation of the underlying source code, in the method search process if the last IMP or not found, will call forward_IMP

Forward_imp = _objc_msgForward_impcache, source code view under _objc_msgForward_impcache implementation, global search _objc_msgForward_impcache

    STATIC_ENTRY __objc_msgForward_impcache

    // No stret specialization.
    b	__objc_msgForward  / / jump __objc_msgForward

    END_ENTRY __objc_msgForward_impcache

    ENTRY __objc_msgForward

    adrp	x17, __objc_forward_handler@PAGE
    ldr	p17, [x17, __objc_forward_handler@PAGEOFF]
    TailCallFunctionPointer x17

    END_ENTRY __objc_msgForward
    
    ...
    .macro TailCallFunctionPointer
	// $0 = function pointer value
	br	$0    / / jump imp
    .endmacro
    ...
Copy the code
  • __objc_msgForward_impcacheThe bottom layer is assembly implementation, the main code b __objc_msgForward
  • __objc_msgForwardIn theTailCallFunctionPointerIs a macro, previously explored is jumpimp.x17The register storesimpAs can be seen from the compilationx17What matters is that__objc_forward_handler
  • Global search__objc_forward_handlerIf there is no concrete implementation in the assembly, then it is not in the assemblyC/C++Source code, global searchobjc_forward_handler, the source code is as follows
// Default forward handler halts the process.
__attribute__((noreturn, cold)) void
objc_defaultForwardHandler(id self, SEL sel)
{
    _objc_fatal("%c[%s %s]: unrecognized selector sent to instance %p "
                "(no message forward handler is installed)".class_isMetaClass(object_getClass(self)) ? '+' : The '-'.object_getClassName(self), sel_getName(sel), self);
}
void *_objc_forward_handler = (void*)objc_defaultForwardHandler;
Copy the code

Error reported in _objc_FATAL familiar with the classic crash message error method is not implemented

In the fast and slow search process did not find IMP, do not directly crash, do not give a chance. No must give a chance, or I am not convinced, the system or stem but I ha, give a chance is dynamic method resolution

Dynamic method resolution

In lookUpImpOrForward, if an IMP is not found, the process resolveMethod_locked is dynamically resolved

NEVER_INLINE
IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior){...// No implementation found. Try method resolver once.
  // If the query method is not implemented, the system attempts a method resolution
  if (slowpath(behavior & LOOKUP_RESOLVER)) {
      behavior ^= LOOKUP_RESOLVER;
      // Dynamic method resolution
      return resolveMethod_locked(inst, sel, cls, behavior); }... }Copy the code

ResolveMethod_locked dynamic method resolution is locked

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

    runtimeLock.unlock(a);// Determine whether the CLS class is a metaclass, if the class specification indicates that the instance method is called
    if (! cls->isMetaClass()) {
        // try [cls resolveInstanceMethod:sel]
        resolveInstanceMethod(inst, sel, cls);
    } 
    else { // If it is a metaclass, the class method is called
        // try [nonMetaClass resolveClassMethod:sel]
        // and [cls resolveInstanceMethod:sel]
        resolveClassMethod(inst, sel, cls);
        // If not, look in the object method of the metaclass, which is equivalent to the object method in the metaclass
        if (!lookUpImpOrNilTryCache(inst, sel, cls)) {
            resolveInstanceMethod(inst, sel, cls); }}// chances are that calling the resolver have populated the cache
    // so attempt using it
    // The imp that corresponds to sel fast lookup and sel slow lookup is actually retrieved from the cache, because it has been cached previously
    return lookUpImpOrForwardTryCache(inst, sel, cls, behavior);
}
Copy the code
  • In the first place to judgeclsWhether it isThe metaclass
  • If it is notThe metaclassJust a normal class, then the instance method of the call jumpsresolveInstanceMethodprocess
  • If it isThe metaclass, then the class method jump is calledresolveClassMethodprocess
  • lookUpImpOrForwardTryCacheFast lookup and slow lookupselThe correspondingimpAnd then returnimp

resolveInstanceMethodmethods

static void resolveInstanceMethod(id inst, SEL sel, Class cls)
{   // inST object // CLS class
    runtimeLock.assertUnlocked(a);ASSERT(cls->isRealized());
    SEL resolve_sel = @selector(resolveInstanceMethod:);
    // As long as the CLS metaclass initialization resolve_sel method must be implemented because NSObject implements resolveInstanceMethod by default
    // The purpose is to cache the resolveInstanceMethod method into the CLS metaclass
    Resolve_sel is a class method based on lookUpImpOrNilTryCache
    if (!lookUpImpOrNilTryCache(cls, resolve_sel, cls->ISA(/*authenticated*/true))) {
        // Resolver not implemented.
        return;
    }
    // Call the resolveInstanceMethod method to send a message
    // Send the message via objc_msgSend to a CLS method
    BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
    // check whether the resolve_sel method is implemented
    bool resolved = msg(cls, resolve_sel, sel);

    
    // Why lookUpImpOrNilTryCache is called
    // The resolveInstanceMethod method is called. But it does not necessarily implement sel methods
    Imp = forward_imp = forward_imp = forward_imp
    // imp = forward_IMP goes down and down_unlock if the slow lookup process dynamic resolution method has passed
    IMP imp = lookUpImpOrNilTryCache(inst, sel, cls);
     
    // Resolved and IMP are dynamically added
    if (resolved  &&  PrintResolving) {
        if (imp) {
            ...
        }
        else {
            // Method resolver didn't add anything?. }}}Copy the code
  • First createresolveInstanceMethod theSEL resolve_sel
  • According to thelookUpImpOrNilTryCache (cls, resolve_sel, cls->ISA(true))knowresolveInstanceMethodAre class methods that are found by fast and slow lookup processesresolve_selThe correspondingimpThe cacheresolveInstanceMethodmethods
  • Directly throughmsg(cls, resolve_sel, sel)Send a message to the class, as you can see hereresolveInstanceMethodIs a class method

LookUpImpOrNilTryCache (INST, SEL, CLS) Fast and slow lookup processes

  • throughlookUpImpOrNilTryCacheTo determine theresolveInstanceMethodIs there an implementation in the methodselThe correspondingimp
  • If it’s implemented, it’s not in cache, go inlookUpImpOrForwardFind theselThe correspondingimpInsert cache, callimpEnd of the search process
  • If it’s not implemented, it’s not in cache, enterlookUpImpOrForwardLook for,selI didn’t find a matchimpAt this time,imp = forward_imp.Dynamic method resolutionIt’s only called once, and it goesdone_unlockanddoneThe process,selandforward_impThe cache is inserted for message forwarding

resolveInstanceMethodmethods

static void resolveClassMethod(id inst, SEL sel, Class cls)
{
    runtimeLock.assertUnlocked(a);ASSERT(cls->isRealized());
    ASSERT(cls->isMetaClass());
    // inST class // CLS metaclass
    NSObject implements the resolveClassMethod method by default
    if (!lookUpImpOrNilTryCache(inst, @selector(resolveClassMethod:), cls)) {
        // Resolver not implemented.
        return;
    }
    / / return to class
    Class nonmeta;
    {
        mutex_locker_t lock(runtimeLock);
        nonmeta = getMaybeUnrealizedNonMetaClass(cls, inst);
        // +initialize path should have realized nonmeta already
        if(! nonmeta->isRealized()) {
            _objc_fatal("nonmeta class %s (%p) unexpectedly not realized",
                        nonmeta->nameForLogging(), nonmeta); }}BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
    // Send a message to the class
    bool resolved = msg(nonmeta, @selector(resolveClassMethod:), sel);
    // Class methods are equivalent to instance methods in metaclasses, both fast and slow look-ups
    IMP imp = lookUpImpOrNilTryCache(inst, sel, cls);

    if (resolved  &&  PrintResolving) {
        if (imp) {
            ...
        }
        else {
            // Method resolver didn't add anything?. }}}Copy the code
  • resolveClassMethodinNSobjectAs long as the metaclass initialization is done, the purpose is to cache in the metaclass
  • callresolveClassMethodClass method, the purpose of which is to make possibleResolveClassMethod dynamically implements SEL corresponding IMP
  • imp = lookUpImpOrNilTryCache(inst, sel, cls)The cacheselThe correspondingimp, no matterimpThere is no dynamic addition, if there is no cacheforward_imp

lookUpImpOrNilTryCachemethods

LookUpImpOrNilTryCache method name, which looks for IMP or nil as far as possible by querying the cache, LookUpImpOrNilTryCache is called in both the resolveInstanceMethod and resolveClassMethod methods

extern IMP lookUpImpOrNilTryCache(id obj, SEL, Class cls, int behavior = 0);

 IMP lookUpImpOrNilTryCache(id inst, SEL sel, Class cls, int behavior)
{
    / / LOOKUP_NIL = 4 no parameter behaviors = 0 0 | 4 = 4
    return _lookUpImpTryCache(inst, sel, cls, behavior | LOOKUP_NIL);
}
Copy the code

First of all, the last parameter is the default behaviors = 0, LOOKUP_NIL = 4, behaviors | LOOKUP_NIL greater than or equal to LOOKUP_NIL

ALWAYS_INLINE
static IMP _lookUpImpTryCache(id inst, SEL sel, Class cls, int behavior)
{
    runtimeLock.assertUnlocked(a);// Whether the CLS is initialized
    if (slowpath(! cls->isInitialized())) {
        // Look up lookUpImpOrForward without it being initialized
        return lookUpImpOrForward(inst, sel, cls, behavior);
    }
    // Find sel imp in cache
    IMP imp = cache_getImp(cls, sel);
    // IMP has a value to enter the done process
    if(imp ! =NULL) goto done;
#if CONFIG_USE_PREOPT_CACHES
    // Whether there is a shared cache
    if (fastpath(cls->cache.isConstantOptimizedCache(/* strict */true))) {
        imp = cache_getImp(cls->cache.`preoptFallbackClass(), sel); } `#endif
    // Imp is not found in the cache and enters the slow search process
    // behavior = 4, 4&2 = 0
    if (slowpath(imp == NULL)) {
        return lookUpImpOrForward(inst, sel, cls, behavior);
    }

done:
    //(behavior & LOOKUP_NIL) = 4 & 4 = 1
    //LOOKUP_NIL just writes to the cache with _objc_msgForward_impcache
    if ((behavior & LOOKUP_NIL) && imp == (IMP)_objc_msgForward_impcache) {
        return nil;
    }
    return imp;
}
Copy the code

To determine whether the CLS is initialized, it will be initialized

Find in cache

  • Look it up in the cacheselThe correspondingimp
  • ifimpThere is jumpdoneprocess
  • Check whether there is a shared cache for the underlying system library
  • If no query is found in the cacheimpTo enter the slow search process

Slow search process

  • In the slow search process,behavior= 44 & 2 = 0Go into dynamic method resolution, so it doesn’t loop all the time
  • The most important if not query to this pointimp= forward_impJump,lookUpImpOrForwardIn thedone_unlockanddoneProcess, insert cache, returnforward_imp

The done process

  • doneProcess: (behavior & LOOKUP_NIL) andimp = _objc_msgForward_impcache, if the cache isforward_imp, and return directlynil, otherwise returnimp.LOOKUP_NILThe condition is to find if it’s dynamically addedimpAnd then there is theimpInserted into the cache

LookUpImpOrNilTryCache uses LOOKUP_NIL to control insertion into the cache, whether sel imp is implemented or not, and if IMP returns a value, it must be implemented dynamically in a dynamic method resolution

resolveInstanceMethodInstance to explore

implementationresolveInstanceMethodmethods

Add the resolveInstanceMethod method to the LWPerson class

int main(int argc, const char * argv[]) {
    @autoreleasepool {
    
        LWPerson * person = [LWPerson alloc];
        [person sayHello];
  
    }
    return 0;
}
Copy the code
@implementation LWPerson

+(BOOL)resolveInstanceMethod:(SEL)sel{
    NSLog(@"- into the % @ -".NSStringFromSelector(sel));
    return [super resolveInstanceMethod:sel];
}
@end
Copy the code
202107 -- 03 12:57:22.582805+0800 KCObjcBuild[7949:330351[sayHello--202107 -- 03 12:57:22.583624+0800 KCObjcBuild[7949:330351[sayHello--Copy the code

The resolveInstanceMethod method was actually called before the crash

The resolution system automatically sends a message to the resolveInstanceMethod (resolveInstanceMethod).

The first time you call the stack of resolveInstanceMethod, you can see that you are following the dynamic resolution method of the slow lookup process

The stack information of resolveInstanceMethod is called the second time, which is called by CoreFoundation, the underlying system library. After the message forwarding is completed, the slow search process is started again, and the dynamic method resolution is entered, and the stack information of resolveInstanceMethod is called again, so there are two times in total. The detailed flow of the second call is described in more detail later

Dynamically addsayHellomethods

@implementation LWPerson

+(BOOL)resolveInstanceMethod:(SEL)sel{

    NSLog(@"- into the % @ -".NSStringFromSelector(sel));
    if (@selector(sayHello) == sel) {
        IMP imp = class_getMethodImplementation(self , @selector(sayHello2));
        Method meth = class_getInstanceMethod(self , @selector(sayHello2));
        const char * type = method_getTypeEncoding(meth);
        return  class_addMethod(self ,sel, imp, type);;
    }
    return [super resolveInstanceMethod:sel];
}

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

@end
Copy the code
  • resolveInstanceMethodOnly called once, because it is added dynamicallysayHellomethodslookUpImpOrForwardTryCacheDirect access toimp, directly callimp, the search process ends
  • The crash also resolves the dynamic method resolution system to give it a chance
  • Specific process:resolveMethod_locked–> resolveInstanceMethod— > callresolveInstanceMethod –> lookUpImpOrNilTryCache(inst, sel, cls) –> lookUpImpOrForwardTryCache— > callimp

resolveClassMethodInstance to explore

implementationresolveClassMethodmethods

Add the resolveInstanceMethod method to the LWPerson class without dynamically implementing the test method

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        LWPerson * person = [LWPerson alloc];
        [LWPerson test];  
    }
    return 0;
}
Copy the code
@implementation LWPerson
+(BOOL)resolveClassMethod:(SEL)sel{
    NSLog(@"- into the % @ -".NSStringFromSelector(sel));
    return [super resolveClassMethod:sel];
}
@end
Copy the code
202107 -- 03 13:51:22.784897+0800 KCObjcBuild[8483:359482] -- enter test--202107 -- 03 13:51:22.785774+0800 KCObjcBuild[8483:359482] -- enter test--Copy the code
  • It did call just before the crashresolveClassMethodMethod, and called twice, the logical sum of two callsresolveInstanceMethodMethod calls are the same twice
  • callresolveClassMethodIn the future, I’ll look it uplookUpImpOrNilTryCacheThere is no concrete dynamic implementationselThe correspondingimpIn the metaclass cache at this timeselThe correspondingimptheimpisforward_imp.lookUpImpOrNilTryCacheThere’s a judgment straight back in therenilAt this point directly toresolveInstanceMethodBecause class methods are actually instance methods in metaclass
  • If it doesn’t happenlookUpImpOrForwardTryCacheAccess to theforward_impThe message forwarding process is displayed

Dynamically addtestmethods

+(BOOL)resolveClassMethod:(SEL)sel{
  if (@selector(test) == sel) {
    NSLog(@"ResolveClassMethod - enter the % @ -".NSStringFromSelector(sel));
    IMP imp = class_getMethodImplementation(object_getClass([self class]), @selector(newTest));
    Method meth = class_getClassMethod(object_getClass([self class]) , @selector(newTest));
    const char * type = method_getTypeEncoding(meth);
    return  class_addMethod(object_getClass([self class]) ,sel, imp, type);;
  }
    return[super resolveClassMethod:sel]; } + (void)newTest{
    NSLog(@"--%s---",__func__);
}
Copy the code
202107 -- 03 16:14:04.262688+0800 KCObjcBuild[9964:434208] resolveClassMethod-- enter test--202107 -- 03 16:14:04.263331+0800 KCObjcBuild[9964:434208] --+[LWPerson newTest]---
Copy the code
  • resolveClassMethodOnly called once, because it is added dynamicallytestmethods
  • resolveClassMethodandresolveInstanceMethodThe call process is basically the same ifresolveClassMethodNo query was called onceresolveInstanceMethodcall

resolveClassMethodspecial

Since the call to resolveClassMethod is not found, and the call to resolveInstanceMethod is found, both resolveClassMethod and resolveInstanceMethod should be implemented in LWPerson class. Theoretically any method can be called

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        LWPerson * person = [LWPerson alloc];
        [LWPerson test];  
    }
    return 0;
}
Copy the code
@implementation LWPerson
+(BOOL)resolveClassMethod:(SEL)sel{
    NSLog(@"ResolveClassMethod - enter the % @ -".NSStringFromSelector(sel));
    return [super resolveClassMethod:sel];
}

+(BOOL)resolveInstanceMethod:(SEL)sel{

    NSLog(@"ResolveInstanceMethod - enter the % @ -".NSStringFromSelector(sel));
    return [super resolveInstanceMethod:sel];
}
@end
Copy the code
202107 -- 03 14:52:55.076902+0800 KCObjcBuild[9158:391254] resolveClassMethod-- enter test--202107 -- 03 14:52:55.077890+0800 KCObjcBuild[9158:391254] resolveClassMethod-- enter test--Copy the code

The result is different from what we expected. Only the resolveClassMethod in LWPerson class is called, but the resolveInstanceMethod method of LWPerson class is not called. The source code shows that the resolveInstanceMethod method is called, so trace the source code

instisLWPersonClass,clsisLWPersonOf the classThe metaclass

LookUpImpOrNilTryCache the argument CLS ->ISA(true) is the root class that enters lookUpImpOrNilTryCache

At this point, INST is the metaclass of LWPerson class, and CLS is the root class. Fast lookup and slow lookup refer to the root metaclass, which means that the metaclass calls the instance method

msg(cls, resolve_sel, sel)And you can verify thatobjc_msgSendSend messages without distinction-and+Methods.objc_msgSendThe receiver ofclsisThe metaclassMeans likeThe metaclassSend a message, the message lookup will findA metaclassTo find out, soresolveInstanceMethodinThe metaclassIs called, so in the classresolveInstanceMethodMethod will not be called, not thatThe metaclassandclassThe name is the same, but the address is different

Create NSObject + LW and add the resolveInstanceMethod method to the class, because the parent of the root metaclass is the root class. The root metaclass cannot be found in the root class, so only the root class can be used because the root metaclass cannot be created

@implementation NSObject (LW)
+(BOOL)resolveInstanceMethod:(SEL)sel{
    if (@selector(test) == sel) {
        NSLog(@"ResolveInstanceMethod - enter the % @ -".NSStringFromSelector(sel));
    }
    return NO;
}
@end
Copy the code
202107 -- 03 16:00:21.999137+0800 KCObjcBuild[9755:425282] resolveClassMethod-- enter test--202107 -- 03 16:00:22.000222+0800 KCObjcBuild[9755:425282] resolveInstanceMethod-- enter test--202107 -- 03 16:00:22.000579+0800 KCObjcBuild[9755:425282] resolveClassMethod-- enter test--202107 -- 03 16:00:22.000681+0800 KCObjcBuild[9755:425282] resolveInstanceMethod-- enter test--Copy the code

Call resolveClassMethod and then resolveInstanceMethod twice

integrationDynamic method resolution

If no class method is dynamically added to the resolveClassMethod method, the resolveInstanceMethod in the metaclass is called. Write resolveInstanceMethod to a public class that calls both class methods and instance methods

  • Instance method lookup flow:object –> class— > untilThe root class(NSObject) — – >nil
  • Class method lookup flow:class –> The metaclass— > untilThe root class(NSObject) — – >nil

You end up in the NSObject class, so this common class is the NSObject class

@implementation NSObject (LW)
+(BOOL)resolveInstanceMethod:(SEL)sel{
    if (@selector(sayHello) == sel) {
       NSLog(@"- into the % @ -".NSStringFromSelector(sel));
       IMP imp = class_getMethodImplementation(self , @selector(sayHello2));
       Method meth = class_getInstanceMethod(self , @selector(sayHello2));
       const char * type = method_getTypeEncoding(meth);
       return  class_addMethod(self ,sel, imp, type);;
          
    }else if (@selector(test) == sel){
       NSLog(@"- into the % @ -".NSStringFromSelector(sel));
       IMP imp = class_getMethodImplementation(object_getClass([self class]), @selector(newTest));
        Method meth = class_getClassMethod(object_getClass([self class]) , @selector(newTest));
       const char * type = method_getTypeEncoding(meth);
       return  class_addMethod(object_getClass([self class]) ,sel, imp, type);;
    }
    return NO;
}

- (void)sayHello2{
     NSLog(@"--%s---",__func__); } + (void)newTest{
    NSLog(@"--%s---",__func__);
}

@end
Copy the code
202107 -- 03 16:54:56.295564+0800 KCObjcBuild[10394:453264[sayHello--202107 -- 03 16:54:56.296146+0800 KCObjcBuild[10394:453264] - [NSObject(LW) sayHello2]---
202107 -- 03 16:54:56.296443+0800 KCObjcBuild[10394:453264] -- enter test--202107 -- 03 16:54:56.296615+0800 KCObjcBuild[10394:453264+ [] -NSObject(LW) newTest]---
Copy the code

Instance methods are class method calls, and the system automatically calls the resolveInstanceMethod method, which is consistent with the above exploration. Dynamic methods determine the advantages

  • You can handle method crashes in a unified manner. You can report method crashes to the server or jump to the home page
  • If you have different modules in your project you can make business distinctions by naming them differently
  • This approach is called faceted programmingAOP

Differences between AOP and OOP

  • OOP: In fact, it is the encapsulation of the properties and behaviors of the object. The same functions are extracted and encapsulated separately, with strong dependence and high coupling
  • AOP: is the processing of a step and stage, from which the extraction of the section, there is repeated operation behavior, AOP can be extracted, the use of dynamic proxy, achieve unified maintenance of program functions, dependence is small, the coupling degree is small, aloneAOPThe removal of extracted functionality also has no impact on the main code.AOPMore like a three-dimensional vertical axis, each class in the plane has a common logical passageAOPConcatenated, the various classes in the plane itself have no relation

Follow the flow chart

forward

The fast and slow lookup process is not found, and the dynamic resolution method is not found, so the following will enter the message forwarding process, but the relevant source code is not found in objC4-818.2, the source code provided by CoreFunction is not detailed query. Apple still provides logging assistance

Log auxiliary

LookUpImpOrForward –> log_and_fill_cache –> logMessageSend, enter logMessageSend to see the source code

if (slowpath(objcMsgLogEnabled && implementer)) {
        bool cacheIt = logMessageSend(implementer->isMetaClass(), 
                                      cls->nameForLogging(),
                                      implementer->nameForLogging(), 
                                      sel);
        if(! cacheIt)return;
    }
Copy the code

LogMessageSend can call objcMsgLogEnabled must be YES

bool objcMsgLogEnabled = false;
static int objcMsgLogFD = - 1;

bool logMessageSend(bool isClassMethod,
                    const char *objectsClass,
                    const char *implementingClass,
                    SEL selector)
{
    char	buf[ 1024 ];

    // Create/open the log file
    if (objcMsgLogFD == (- 1))
    {   
        // The path to the file
        snprintf (buf, sizeof(buf), "/tmp/msgSends-%d", (int) getpid ());
        objcMsgLogFD = secure_open (buf, O_WRONLY | O_CREAT, geteuid());
        if (objcMsgLogFD < 0) {
            // no log file - disable logging
            objcMsgLogEnabled = false;
            objcMsgLogFD = - 1;
            return true; }}// Make the log entry
    snprintf(buf, sizeof(buf), "%c %s %s %s\n",
            isClassMethod ? '+' : The '-',
            objectsClass,
            implementingClass,
            sel_getName(selector));

    objcMsgLogLock.lock(a);write (objcMsgLogFD, buf, strlen(buf));
    objcMsgLogLock.unlock(a);// Tell caller to not cache the method
    return false;
}
Copy the code

/ TMP /msgSends is a sandbox path for saving logs. After this parameter is enabled, go to the sandbox path to obtain the file. The default objcMsgLogEnabled = false so find the assignment

void instrumentObjcMessageSends(BOOL flag)
{
    bool enable = flag;

    // Shortcut NOP
    if (objcMsgLogEnabled == enable)
        return;

    // If enabling, flush all method caches so we get some traces
    if (enable)
        _objc_flush_caches(Nil);

    // Sync our log file
    if(objcMsgLogFD ! =- 1)
        fsync (objcMsgLogFD);
   
    objcMsgLogEnabled = enable;
}
Copy the code

Through the instrumentObjcMessageSends to objcMsgLogEnabled assignment, So in need to log information statement instrumentObjcMessageSends extern void instrumentObjcMessageSends (BOOL flag);

extern void instrumentObjcMessageSends(BOOL flag);

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

The message forwarding process following the dynamic resolution method isforwardingTargetForSelectorandmethodSignatureForSelectorThe message forwarding process will be explored later

conclusion

Dynamic method decisions give you one more chance, giving you a chance to implement dynamically, and giving developers a chance to try something new. It has to be said that the process of dynamic method resolution is complex, and it needs careful experience to make it more clear