美文网首页
iOS 源码分析 objc_msgSend class_getI

iOS 源码分析 objc_msgSend class_getI

作者: 孙掌门 | 来源:发表于2020-04-04 15:00 被阅读0次

    iOS 源码分析 objc_msgSend class_getInstanceMethod class_getClassMethod 实现原理

    消息转发我之前文章有写过,网上也有很多博客,但是消息转发的具体流程,方法查找的具体流程到底是什么样的呢,我们今天来看看源码,看看方法查找的具体流程到底是什么。

    class_getInstanceMethod

    当我们调用 这个方法,来查找某个类的实力的方法的时候,我们看看都干了什么事,其实 objc_msgSend 还有 class_getClassMethod 是一样的道理,最后都会调到这里,只不过可能前面不同,比如 objc_msgSend,会调用汇编的方法

    Method class_getInstanceMethod(Class cls, SEL sel)
    {
        if (!cls  ||  !sel) return nil;
    
        // This deliberately avoids +initialize because it historically did so.
    
        // This implementation is a bit weird because it's the only place that 
        // wants a Method instead of an IMP.
    
    #warning fixme build and search caches
            
        // Search method lists, try method resolver, etc.
        lookUpImpOrNil(cls, sel, nil, 
                       NO/*initialize*/, NO/*cache*/, YES/*resolver*/);
    
    #warning fixme build and search caches
    
        return _class_getMethod(cls, sel);
    }
    
    

    我们可以看到调用了lookUpImpOrNil 这个方法,而且苹果还给了注释,// Search method lists, try method resolver, etc.,搜索方法列表和方法解析器,都是什么呢?后面会讲到

    
    /***********************************************************************
    * lookUpImpOrNil.
    * Like lookUpImpOrForward, but returns nil instead of _objc_msgForward_impcache
    **********************************************************************/
    IMP lookUpImpOrNil(Class cls, SEL sel, id inst, 
                       bool initialize, bool cache, bool resolver)
    {
        IMP imp = lookUpImpOrForward(cls, sel, inst, initialize, cache, resolver);
        if (imp == _objc_msgForward_impcache) return nil;
        else return imp;
    }
    

    这个方法调用了另一个方法,关键实现都在这个方法里面

    
    IMP lookUpImpOrForward(Class cls, SEL sel, id inst, 
                           bool initialize, bool cache, bool resolver)
    {
        IMP imp = nil;
        bool triedResolver = NO;
    
        runtimeLock.assertUnlocked();
    
        // Optimistic cache lookup
        if (cache) {
            imp = cache_getImp(cls, sel);
            if (imp) return imp;
        }
    
        // runtimeLock is held during isRealized and isInitialized checking
        // to prevent races against concurrent realization.
    
        // runtimeLock is held during method search to make
        // method-lookup + cache-fill atomic with respect to method addition.
        // Otherwise, a category could be added but ignored indefinitely because
        // the cache was re-filled with the old value after the cache flush on
        // behalf of the category.
    
        runtimeLock.lock();
        checkIsKnownClass(cls);
    
        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 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;
    }
    
    

    我们来一点一点分析,首先

        runtimeLock.assertUnlocked();
    
        // Optimistic cache lookup
        if (cache) {
            imp = cache_getImp(cls, sel);
            if (imp) return imp;
        }
    
        // runtimeLock is held during isRealized and isInitialized checking
        // to prevent races against concurrent realization.
    
        // runtimeLock is held during method search to make
        // method-lookup + cache-fill atomic with respect to method addition.
        // Otherwise, a category could be added but ignored indefinitely because
        // the cache was re-filled with the old value after the cache flush on
        // behalf of the category.
    
        runtimeLock.lock();
        checkIsKnownClass(cls);
    
    

    首先将runtime锁解开,然后进行无锁缓存列表查找,cache_getImp 其实调用的是汇编方法,从代码上我们可以理解为,如果缓存列表中查到了 imp ,那么直接返回。

    接着

    if (!cls->isRealized()) {
            realizeClass(cls);
        }
    
    
    

    检查类是否有初始化,如果没有初始化,那么初始化我们的类。

    
    static Class realizeClassWithoutSwift(Class cls)
    {
        runtimeLock.assertLocked();
    
        const class_ro_t *ro;
        class_rw_t *rw;
        Class supercls;
        Class metacls;
        bool isMeta;
    
        if (!cls) return nil;
        if (cls->isRealized()) return cls;
        assert(cls == remapClass(cls));
    
        // fixme verify class is not in an un-dlopened part of the shared cache?
    
        ro = (const class_ro_t *)cls->data();
        if (ro->flags & RO_FUTURE) {
            // This was a future class. rw data is already allocated.
            rw = cls->data();
            ro = cls->data()->ro;
            cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
        } else {
            // Normal class. Allocate writeable class data.
            rw = (class_rw_t *)calloc(sizeof(class_rw_t), 1);
            rw->ro = ro;
            rw->flags = RW_REALIZED|RW_REALIZING;
            cls->setData(rw);
        }
    
        isMeta = ro->flags & RO_META;
    
        rw->version = isMeta ? 7 : 0;  // old runtime went up to 6
    
    
        // Choose an index for this class.
        // Sets cls->instancesRequireRawIsa if indexes no more indexes are available
        cls->chooseClassArrayIndex();
    
        if (PrintConnecting) {
            _objc_inform("CLASS: realizing class '%s'%s %p %p #%u %s%s",
                         cls->nameForLogging(), isMeta ? " (meta)" : "", 
                         (void*)cls, ro, cls->classArrayIndex(),
                         cls->isSwiftStable() ? "(swift)" : "",
                         cls->isSwiftLegacy() ? "(pre-stable swift)" : "");
        }
    
        // Realize superclass and metaclass, if they aren't already.
        // This needs to be done after RW_REALIZED is set above, for root classes.
        // This needs to be done after class index is chosen, for root metaclasses.
        // This assumes that none of those classes have Swift contents,
        //   or that Swift's initializers have already been called.
        //   fixme that assumption will be wrong if we add support
        //   for ObjC subclasses of Swift classes.
        supercls = realizeClassWithoutSwift(remapClass(cls->superclass));
        metacls = realizeClassWithoutSwift(remapClass(cls->ISA()));
    
    #if SUPPORT_NONPOINTER_ISA
        // Disable non-pointer isa for some classes and/or platforms.
        // Set instancesRequireRawIsa.
        bool instancesRequireRawIsa = cls->instancesRequireRawIsa();
        bool rawIsaIsInherited = false;
        static bool hackedDispatch = false;
    
        if (DisableNonpointerIsa) {
            // Non-pointer isa disabled by environment or app SDK version
            instancesRequireRawIsa = true;
        }
        else if (!hackedDispatch  &&  !(ro->flags & RO_META)  &&  
                 0 == strcmp(ro->name, "OS_object")) 
        {
            // hack for libdispatch et al - isa also acts as vtable pointer
            hackedDispatch = true;
            instancesRequireRawIsa = true;
        }
        else if (supercls  &&  supercls->superclass  &&  
                 supercls->instancesRequireRawIsa()) 
        {
            // This is also propagated by addSubclass() 
            // but nonpointer isa setup needs it earlier.
            // Special case: instancesRequireRawIsa does not propagate 
            // from root class to root metaclass
            instancesRequireRawIsa = true;
            rawIsaIsInherited = true;
        }
        
        if (instancesRequireRawIsa) {
            cls->setInstancesRequireRawIsa(rawIsaIsInherited);
        }
    // SUPPORT_NONPOINTER_ISA
    #endif
    
        // Update superclass and metaclass in case of remapping
        cls->superclass = supercls;
        cls->initClassIsa(metacls);
    
        // Reconcile instance variable offsets / layout.
        // This may reallocate class_ro_t, updating our ro variable.
        if (supercls  &&  !isMeta) reconcileInstanceVariables(cls, supercls, ro);
    
        // Set fastInstanceSize if it wasn't set already.
        cls->setInstanceSize(ro->instanceSize);
    
        // Copy some flags from ro to rw
        if (ro->flags & RO_HAS_CXX_STRUCTORS) {
            cls->setHasCxxDtor();
            if (! (ro->flags & RO_HAS_CXX_DTOR_ONLY)) {
                cls->setHasCxxCtor();
            }
        }
        
        // Propagate the associated objects forbidden flag from ro or from
        // the superclass.
        if ((ro->flags & RO_FORBIDS_ASSOCIATED_OBJECTS) ||
            (supercls && supercls->forbidsAssociatedObjects()))
        {
            rw->flags |= RW_FORBIDS_ASSOCIATED_OBJECTS;
        }
    
        // Connect this class to its superclass's subclass lists
        if (supercls) {
            addSubclass(supercls, cls);
        } else {
            addRootClass(cls);
        }
    
        // Attach categories
        methodizeClass(cls);
    
        return cls;
    }
    
    

    关于这块,我的前一篇文章,class的本质 中有写到,就是讲方法添加到我们类的方法列表中,并且也会递归创建我们的父类和元类,然后

    
    imp = cache_getImp(cls, sel);
        if (imp) goto done;
    
    

    在当前类的缓存中查找是否有 imp ,

     if (imp) goto done;
    
        // Try this class's method lists.
        {
            Method meth = getMethodNoSuper_nolock(cls, sel);
            if (meth) {
                log_and_fill_cache(cls, meth->imp, sel, inst, cls);
                imp = meth->imp;
                goto done;
            }
        }
    
    

    如果缓存中没有,就去方法列表中查找

    
    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;
    }
    
    static method_t *search_method_list(const method_list_t *mlist, SEL sel)
    {
        int methodListIsFixedUp = mlist->isFixedUp();
        int methodListHasExpectedSize = mlist->entsize() == sizeof(method_t);
        
        if (__builtin_expect(methodListIsFixedUp && methodListHasExpectedSize, 1)) {
            return findMethodInSortedMethodList(sel, mlist);
        } else {
            // Linear search of unsorted method list
            for (auto& meth : *mlist) {
                if (meth.name == sel) return &meth;
            }
        }
    
    #if DEBUG
        // sanity-check negative results
        if (mlist->isFixedUp()) {
            for (auto& meth : *mlist) {
                if (meth.name == sel) {
                    _objc_fatal("linear search worked when binary search did not");
                }
            }
        }
    #endif
    
        return nil;
    }
    
    
    
    static method_t *findMethodInSortedMethodList(SEL key, const method_list_t *list)
    {
        assert(list);
    
        const method_t * const first = &list->first;
        const method_t *base = first;
        const method_t *probe;
        uintptr_t keyValue = (uintptr_t)key;
        uint32_t count;
        
        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--;
            }
        }
        
        return nil;
    }
    
    

    利用的是二分查找,从方法列表中,如果找到了,返回 method_t,然后调用 log_and_fill_cache ,加入到他的缓存列表中去,以便于下次查找,接下来

    
       // Try superclass caches and method lists.
        {
            unsigned attempts = unreasonableClassCount();
            for (Class curClass = cls->superclass;
                 curClass != nil;
                 curClass = curClass->superclass)
            {
    
    

    可以看到,去他的superClass 里面查找,

    
      
                // 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;
                }
    
    

    然后和上面一样,先去父类的缓存中查找,如果查找到了,添加到父类的缓存列表中去

    如果在父类中也没有查找到,就进行到下一步,消息转发机制

     if (resolver  &&  !triedResolver) {
            runtimeLock.unlock();
            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;
        }
    
    
    
    
    static void resolveMethod(Class cls, SEL sel, id inst)
    {
        runtimeLock.assertUnlocked();
        assert(cls->isRealized());
    
        if (! cls->isMetaClass()) {
            // try [cls resolveInstanceMethod:sel]
            resolveInstanceMethod(cls, sel, inst);
        } 
        else {
            // try [nonMetaClass resolveClassMethod:sel]
            // and [cls resolveInstanceMethod:sel]
            resolveClassMethod(cls, sel, inst);
            if (!lookUpImpOrNil(cls, sel, inst, 
                                NO/*initialize*/, YES/*cache*/, NO/*resolver*/)) 
            {
                resolveInstanceMethod(cls, sel, inst);
            }
        }
    }
    
    

    其实就是判断是否实现了,resolveInstanceMethodresolveClassMethod 方法,里面其实调用的还是 objc_msgSend,查看是否实现了这个方法,如果是 resolveClassMethod 会瞎按查找是否实现了 class method,然后在尝试查找一次 resolveInstanceMethod,如果没有会在 retry 一次,如果这个方法还没有实现,没关系,还有下一步

    
    
        // No implementation found, and method resolver didn't help. 
        // Use forwarding.
    
        imp = (IMP)_objc_msgForward_impcache;
        cache_fill(cls, sel, imp, inst);
    
    

    其实就是判断是否实现了 forwardInvocation 这个方法,如果找到就添加到当前缓存中去,

    这就是我们整个方法查找的过程

    我们在深入探索一下 resolveMethod 这个方法,当我们不是元类的时候,会调用这个方法

    
    static void _class_resolveInstanceMethod(Class cls, SEL sel, id inst)
    {
        Class met = cls->ISA();
        if (! lookUpImpOrNil(cls->ISA(), SEL_resolveInstanceMethod, cls, 
                             NO/*initialize*/, YES/*cache*/, NO/*resolver*/)) 
        {
            // Resolver not implemented.
            return;
        }
    
        BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
        bool resolved = msg(cls, SEL_resolveInstanceMethod, sel);
    
        // Cache the result (good or bad) so the resolver doesn't fire next time.
        // +resolveInstanceMethod adds to self a.k.a. cls
        IMP imp = lookUpImpOrNil(cls, sel, inst, 
                                 NO/*initialize*/, YES/*cache*/, NO/*resolver*/);
    
        if (resolved  &&  PrintResolving) {
            if (imp) {
                _objc_inform("RESOLVE: method %c[%s %s] "
                             "dynamically resolved to %p", 
                             cls->isMetaClass() ? '+' : '-', 
                             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() ? '+' : '-', 
                             cls->nameForLogging(), sel_getName(sel));
            }
        }
    }
    
    

    这个方法首先会检测,是否实现了 SEL_resolveInstanceMethod 这个方法,也就是 resolveInstanceMethod ,,会沿着继承链一直上找,知道找到nsobject,如果我们实现了,会通过调用 objc_msgSend 向我们的类发送消息,调用这个方法,然后接下来,会 lookUpImpOrNil
    调用这个方法,因为我们可能在 那个方法里面动态添加方法,这时候再去查找,就能查找到了,如果继承连都没有找到,这时候会走我们的消息转发流程,也就是 forward,这个苹果是闭源的,会为我们再次调用此意 resolveInstanceMethod

    我们再看是元类的情况下

     else {
            // try [nonMetaClass resolveClassMethod:sel]
            // and [cls resolveInstanceMethod:sel]
            resolveClassMethod(cls, sel, inst);
            if (!lookUpImpOrNil(cls, sel, inst, 
                                NO/*initialize*/, YES/*cache*/, NO/*resolver*/)) 
            {
                resolveInstanceMethod(cls, sel, inst);
            }
        }
    
    
    

    首先会调用 resolveClassMethod,然后发送消息,如果找不到,因为按照继承连最后都会找到 nsobject,元类的父类为根源类,根源类通过isa也是指向自己,而根源类的父类为 nsobject,所以,看源码,最后是去nsobject调用了 resolveInstanceMethod 这个方法。

    相关文章

      网友评论

          本文标题:iOS 源码分析 objc_msgSend class_getI

          本文链接:https://www.haomeiwen.com/subject/firjphtx.html