7, iOS underlying analysis - news Find Process

Find flow method

  1. Object test method
    1. Examples of methods of an object - they have
    2. Examples of methods of an object - did not - look for the parent class
    3. Examples of methods of an object - it did not - no parent - the parent class to find the parent class - NSObject
    4. Examples of methods of an object - it did not - no parent - the parent class to find the parent class - NSObject no - collapse
    5. Summary: SelfClass -> SuperClass -> ...-> NSObject -> nil
  2. Class method of testing
    1. Class methods - have
    2. Class methods - we did not - have a parent class
    3. Class methods - I did not - no parent - the parent class parent - NSObject
    4. Class methods - he did not - no parent - the parent class of the parent class - NSObject no - collapse
    5. Class methods - he did not - no parent - the parent class of the parent class - NSObject did not - but there is an object method, the object method call
    6. 总结: Self Meta Class -> Super Meta Class -> ... -> NSObjectMetaClass-> NSObject -> nil
    7. This has a point, the method is in the form class instances (objects) in the method of the metaclass, so e.g. LGPerson call a method of NSObject instance, this is also possible.
  3. Dynamic method resolution
  4. Message forwarding
  5. If not, an error crash
    1. Unrecognized selector sent to instance
2020-01-20 21:03:41.414708+0800 LGTest[1213:36305] 
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', 
reason: '-[LGPerson sayHello]: unrecognized selector sent to instance 0x101848d30'

Process Analysis

objc_msgSend First cache to quickly find, if not, then look for a slower routine

Quick Look is the process of sending a message the previous analysis process, so for now _class_lookupMethodAndLoadCache3 normal lookup process

objc_calss_old.mm 中
IMP _class_lookupMethodAndLoadCache3(id obj, SEL sel, Class cls)
{        
    return lookUpImpOrForward(cls, sel, obj, 
                              YES/*initialize*/, NO/*cache*/, YES/*resolver*/);
}

lookUpImpOrForward analysis

This method is very important, we must remember

  • First, a series of preparations to ensure that when an object method or a class method is not found, or can have a parent class metaclass can continue to let us go to find a series of recursive

Slow search, not found by obcj_msgSend quickly find, there needs to be a slow look like

  • IMP lookUpImpOrForward(Class cls, SEL sel, id inst, 
                           bool initialize, bool cache, bool resolver)
    {
        IMP imp = nil;
        bool triedResolver = NO;
    
        runtimeLock.assertUnlocked();
    
        传进来的cache 是NO 所以不用看这一部分
        接下来是判断是否是非法的类,判断也不用看
    
        if (!cls->isRealized()) {
            realizeClass(cls);
            准备条件。如果类没有实现,需要重现实现。准备类(包括父类和元类)
        }
    
        if (initialize  &&  !cls->isInitialized()) {
            runtimeLock.unlock();
            _class_initialize (_class_getNonMetaClass(cls, inst));
            runtimeLock.lock();
            // If sel == initialize, _class_initialize will send +initialize and 
            // then the messenger will send +initialize again after this 
            // procedure finishes. Of course, if this is not being called 
            // from the messenger then it won't happen. 2778172
        }
    
        
     retry:    
        runtimeLock.assertLocked();
    
        // Try this class's cache.
    
        imp = cache_getImp(cls, sel);
        if (imp) goto done;
    
    下边加括号的目的是 实现局部作用域,两个作用域内的变量名重复不会冲突
        // Try this class's method lists.
        {
            去当前类的method_list 中查找方法
            Method meth = getMethodNoSuper_nolock(cls, sel);
            if (meth) {
                log_and_fill_cache(cls, meth->imp, sel, inst, cls);
                imp = meth->imp;
                goto done;
            }
        }
    
        // Try superclass caches and method lists.
        {
            当前类的父类中的查找方法
            unsigned attempts = unreasonableClassCount();
            for (Class curClass = cls->superclass;
                 curClass != nil;
                 curClass = curClass->superclass)
            {
                // Halt if there is a cycle in the superclass chain.
                if (--attempts == 0) {
                    _objc_fatal("Memory corruption in class list.");
                }
                
                // Superclass cache.
                imp = cache_getImp(curClass, sel);
                if (imp) {
                    if (imp != (IMP)_objc_msgForward_impcache) {
                        // Found the method in a superclass. Cache it in this class.
                        log_and_fill_cache(cls, imp, sel, inst, curClass);
                        goto done;
                    }
                    else {
                        // Found a forward:: entry in a superclass.
                        // Stop searching, but don't cache yet; call method 
                        // resolver for this class first.
                        break;
                    }
                }
                
                // Superclass method list.
                Method meth = getMethodNoSuper_nolock(curClass, sel);
                if (meth) {
                    log_and_fill_cache(cls, meth->imp, sel, inst, curClass);
                    imp = meth->imp;
                    goto done;
                }
            }
        }
    
        // No implementation found. Try method resolver once.
    
        if (resolver  &&  !triedResolver) {
            runtimeLock.unlock();
            _class_resolveMethod(cls, sel, inst);
            runtimeLock.lock();
            // Don't cache the result; we don't hold the lock so it may have 
            // changed already. Re-do the search from scratch instead.
            triedResolver = YES;
            goto retry;
        }
    
        // No implementation found, and method resolver didn't help. 
        // Use forwarding.
    
        imp = (IMP)_objc_msgForward_impcache;
        cache_fill(cls, sel, imp, inst);
    
     done:
        runtimeLock.unlock();
    
        return imp;
    }

To prepare a class (parent class and including metaclass), then the parent class or classes method_list lookup method. First determine what the current imp has not cached, then the cache if there is a direct call to (this time may have come in the cache)

Following the adoption of getMethodNoSuper_nolock method list to find

static Class realizeClass(Class cls)
{
    runtimeLock.assertLocked();

    const class_ro_t *ro;
    class_rw_t *rw;
    Class supercls;
    Class metacls;
    bool isMeta;
   。。。。。。
    
    supercls = realizeClass(remapClass(cls->superclass));  递归实现父类
    metacls = realizeClass(remapClass(cls->ISA()));        递归实现元类

#if SUPPORT_NONPOINTER_ISA
。。。。。。
// SUPPORT_NONPOINTER_ISA
#endif
    。。。。。
    // Attach categories
    methodizeClass(cls);

    return cls;
}

getMethodNoSuper_nolock

In getMethodNoSuper_nolock internal methods, by   methods   list is traversed find the current sel corresponding Method, , if not found, it returns nil

static method_t *
getMethodNoSuper_nolock(Class cls, SEL sel)
{
    runtimeLock.assertLocked();

    assert(cls->isRealized());
    // fixme nil cls? 
    // fixme nil sel?

    for (auto mlists = cls->data()->methods.beginLists(), 
              end = cls->data()->methods.endLists(); 
         mlists != end;
         ++mlists)
    {
        method_t *m = search_method_list(*mlists, sel);
        if (m) return m;
    }

    return nil;
}

In  search_method_list   after the procedure, by  findMethodInSortedMethodList (sel, mlist)   binary search, guaranteed to quickly find the target method.

Specific binary search codes

// 关键二分查找代码
for (count = list->count; count != 0; count >>= 1) {
        probe = base + (count >> 1);
        
        uintptr_t probeValue = (uintptr_t)probe->name;
        
        if (keyValue == probeValue) {
            // `probe` is a match.
            // Rewind looking for the *first* occurrence of this value.
            // This is required for correct category overrides.
            while (probe > first && keyValue == (uintptr_t)probe[-1].name) {
                probe--;
            }
            return (method_t *)probe;
        }
        
        if (keyValue > probeValue) {
            base = probe + 1;
            count--;
        }
    }

Find method_t return, if not found to continue to the next step, and then find the cache_fill to cache

The method is found, enter log_and_fill_cache method, called internally cache_fill into the cache process, after entering goto done

Find continue the process of

1) If he did not, to find the parent class.

2), the parent cache look there imp = cache_getImp (cls, sel)

Then there is no way to check the list of parent Method meth = getMethodNoSuper_nolock (cls, sel)

3) If the parent class not found, the method will go to class the parent class metaclass

class curClass = cls -> superclass (parent class metaclass of the parent class is metaclass)

Ibid Find

4) If the parent metaclass class but could not find, and went in to find NSObject

static void
log_and_fill_cache(Class cls, IMP imp, SEL sel, id receiver, Class implementer)
{
#if SUPPORT_MESSAGE_LOGGING
    if (objcMsgLogEnabled) {
        bool cacheIt = logMessageSend(implementer->isMetaClass(), 
                                      cls->nameForLogging(),
                                      implementer->nameForLogging(), 
                                      sel);
        if (!cacheIt) return;
    }
#endif
    cache_fill (cls, sel, imp, receiver);
}

If the above process method is still not found, it will conduct a dynamic method resolution   _class_resolveMethod , in the process, the system will call the two pre-defined a class method that already exists, provide an opportunity for fault tolerance. If you do not want to let the program because the method can not find Ben collapse can be processed here, you can print an error message and so on.

// objc源码
_class_resolveInstanceMethod(cls, sel, inst)
_class_resolveClassMethod(cls, sel, inst)

// NSObject内部方法
+ (BOOL)resolveClassMethod:(SEL)sel OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
+ (BOOL)resolveInstanceMethod:(SEL)sel OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

If the system to these two methods did not have to do any processing, it will enter the stage of message forwarding, message forwarding the global stage search method. Within their compilation method, called __objc_msgForward

imp = (IMP)_objc_msgForward_impcache;
    STATIC_ENTRY __objc_msgForward_impcache

    // No stret specialization.
    b   __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

Global search __objc_forward_handler (compiled in c ++ you want to remove an underscore), found objc_defaultForwardHandler   method implementation 

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)) ? '+' : '-', 
                object_getClassName(self), sel_getName(sel), self);
}
void *_objc_forward_handler = (void*)objc_defaultForwardHandler;

If nothing deal, could not find an error methods will collapse

2020-01-20 21:03:41.414708+0800 LGTest[1213:36305] 
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', 
reason: '-[LGPerson sayHello]: unrecognized selector sent to instance 0x101848d30'

to sum up

  • Find method, first find by the fast cache objc_msgSend, after then look through the conventional dichotomy of class and the parent class method list
  • Finally, if the method did not need to find, you first enter the dynamic method resolution, after entering the fast forwarding and message forwarding routine, the message is forwarded after processing failed error crash

 

Extended:

If that fails methods will collapse, and sometimes we do not want to crash can be handled by the message forwarding mechanism

Process flow into fast way to find Slow Process

Message forwarding mechanism is also divided into fast-track Slow Process

 

 

 

 

 

 

 

 

 

 

 

Published 83 original articles · won praise 12 · views 180 000 +

Guess you like

Origin blog.csdn.net/shengdaVolleyball/article/details/104056265